"""Лимиты и пакеты трафика.""" from __future__ import annotations from dataclasses import dataclass # База на подписку + докупка BASE_TRAFFIC_GB = 1000.0 PACK_GB = 500.0 PACK_PRICE_RUB = 50.0 GB = 1024 * 1024 * 1024 def gb_to_bytes(gb: float) -> float: return float(gb) * GB def bytes_to_gb(value: float | int | None) -> float: if not value: return 0.0 return float(value) / GB def format_gb(gb: float, *, digits: int = 1) -> str: if gb >= 100 and abs(gb - round(gb)) < 0.05: return f"{int(round(gb))} ГБ" if digits <= 0: return f"{int(round(gb))} ГБ" text = f"{gb:.{digits}f}".rstrip("0").rstrip(".") return f"{text} ГБ" def effective_limit_bytes(limit_bytes: float | int | None) -> float: """0 / None в панели = безлимит; для нас база = 1000 ГБ.""" raw = float(limit_bytes or 0) if raw <= 0: return gb_to_bytes(BASE_TRAFFIC_GB) return raw def ensure_base_limit_bytes(current_limit_bytes: float | int | None) -> float: """Не меньше базовых 1000 ГБ; безлимит (0) → база.""" raw = float(current_limit_bytes or 0) base = gb_to_bytes(BASE_TRAFFIC_GB) if raw <= 0: return base return max(raw, base) @dataclass(frozen=True, slots=True) class TrafficPack: packs: int gb: float price: float @property def id(self) -> str: return f"pack_{self.packs}" @property def title(self) -> str: return f"+{format_gb(self.gb, digits=0)}" def format_price(self) -> str: if self.price == int(self.price): return f"{int(self.price)} ₽/мес" return f"{self.price:.2f} ₽/мес" # Готовые пакеты: 1 / 2 / 4 / 10 × 500 ГБ TRAFFIC_PACKS: tuple[TrafficPack, ...] = tuple( TrafficPack( packs=n, gb=PACK_GB * n, price=PACK_PRICE_RUB * n, ) for n in (1, 2, 4, 10) ) def get_traffic_pack(pack_id: str) -> TrafficPack | None: for p in TRAFFIC_PACKS: if p.id == pack_id or str(p.packs) == pack_id: return p return None @dataclass(frozen=True, slots=True) class TrafficUsage: used_bytes: float limit_bytes: float @property def used_gb(self) -> float: return bytes_to_gb(self.used_bytes) @property def limit_gb(self) -> float: return bytes_to_gb(self.limit_bytes) @property def left_gb(self) -> float: return max(0.0, self.limit_gb - self.used_gb) @property def percent(self) -> float: if self.limit_bytes <= 0: return 0.0 return min(100.0, max(0.0, 100.0 * self.used_bytes / self.limit_bytes)) def format_line(self) -> str: return ( f"📦 Трафик: {format_gb(self.used_gb)} / {format_gb(self.limit_gb)} " f"({self.percent:.0f}%)\n" f"Осталось: {format_gb(self.left_gb)}" ) def as_dict(self) -> dict: return { "used_gb": round(self.used_gb, 2), "limit_gb": round(self.limit_gb, 2), "left_gb": round(self.left_gb, 2), "percent": round(self.percent, 1), "used_label": format_gb(self.used_gb), "limit_label": format_gb(self.limit_gb), "left_label": format_gb(self.left_gb), } def traffic_from_panel_user(user) -> TrafficUsage: used = float(getattr(user, "used_traffic_bytes", 0) or 0) limit = effective_limit_bytes(getattr(user, "traffic_limit_bytes", 0)) return TrafficUsage(used_bytes=used, limit_bytes=limit)