Files
remnawave-bedolaga-telegram…/app/utils/formatters.py

230 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from datetime import datetime
def format_datetime(dt: datetime | str, format_str: str = '%d.%m.%Y %H:%M') -> str:
if isinstance(dt, str):
if dt == 'now' or dt == '':
dt = datetime.now()
else:
try:
dt = datetime.fromisoformat(dt.replace('Z', '+00:00'))
except (ValueError, AttributeError):
dt = datetime.now()
return dt.strftime(format_str)
def format_date(dt: datetime | str, format_str: str = '%d.%m.%Y') -> str:
if isinstance(dt, str):
if dt == 'now' or dt == '':
dt = datetime.now()
else:
try:
dt = datetime.fromisoformat(dt.replace('Z', '+00:00'))
except (ValueError, AttributeError):
dt = datetime.now()
return dt.strftime(format_str)
def format_time_ago(dt: datetime | str, language: str = 'ru') -> str:
if isinstance(dt, str):
if dt == 'now' or dt == '':
dt = datetime.now()
else:
try:
dt = datetime.fromisoformat(dt.replace('Z', '+00:00'))
except (ValueError, AttributeError):
dt = datetime.now()
now = datetime.utcnow()
diff = now - dt
language_code = (language or 'ru').split('-')[0].lower()
if diff.days > 0:
if diff.days == 1:
return 'yesterday' if language_code == 'en' else 'вчера'
if diff.days < 7:
value = diff.days
if language_code == 'en':
suffix = 'day' if value == 1 else 'days'
return f'{value} {suffix} ago'
return f'{value} дн. назад'
if diff.days < 30:
value = diff.days // 7
if language_code == 'en':
suffix = 'week' if value == 1 else 'weeks'
return f'{value} {suffix} ago'
return f'{value} нед. назад'
if diff.days < 365:
value = diff.days // 30
if language_code == 'en':
suffix = 'month' if value == 1 else 'months'
return f'{value} {suffix} ago'
return f'{value} мес. назад'
value = diff.days // 365
if language_code == 'en':
suffix = 'year' if value == 1 else 'years'
return f'{value} {suffix} ago'
return f'{value} г. назад'
if diff.seconds > 3600:
value = diff.seconds // 3600
if language_code == 'en':
suffix = 'hour' if value == 1 else 'hours'
return f'{value} {suffix} ago'
return f'{value} ч. назад'
if diff.seconds > 60:
value = diff.seconds // 60
if language_code == 'en':
suffix = 'minute' if value == 1 else 'minutes'
return f'{value} {suffix} ago'
return f'{value} мин. назад'
return 'just now' if language_code == 'en' else 'только что'
def format_days_declension(days: int, language: str = 'ru') -> str:
language_code = (language or 'ru').split('-')[0].lower()
if language_code not in {'ru', 'fa'}:
return f'{days} day{"s" if days != 1 else ""}'
if days % 10 == 1 and days % 100 != 11:
return f'{days} день'
if days % 10 in [2, 3, 4] and days % 100 not in [12, 13, 14]:
return f'{days} дня'
return f'{days} дней'
def format_duration(seconds: int) -> str:
if seconds < 60:
return f'{seconds} сек.'
minutes = seconds // 60
if minutes < 60:
return f'{minutes} мин.'
hours = minutes // 60
if hours < 24:
return f'{hours} ч.'
days = hours // 24
return f'{days} дн.'
def format_bytes(bytes_value: int) -> str:
if bytes_value == 0:
return '0 B'
units = ['B', 'KB', 'MB', 'GB', 'TB']
size = float(bytes_value)
unit_index = 0
while size >= 1024 and unit_index < len(units) - 1:
size /= 1024
unit_index += 1
if size == int(size):
return f'{int(size)} {units[unit_index]}'
return f'{size:.1f} {units[unit_index]}'
def format_percentage(value: float, decimals: int = 1) -> str:
return f'{value:.{decimals}f}%'
def format_number(number: float, separator: str = ' ') -> str:
if isinstance(number, float):
integer_part = int(number)
decimal_part = number - integer_part
formatted_integer = f'{integer_part:,}'.replace(',', separator)
if decimal_part > 0:
return f'{formatted_integer}.{decimal_part:.2f}'.split('.')[0] + f'.{str(decimal_part).split(".")[1][:2]}'
return formatted_integer
return f'{number:,}'.replace(',', separator)
def format_price_range(min_price: int, max_price: int) -> str:
from app.config import settings
min_formatted = settings.format_price(min_price)
max_formatted = settings.format_price(max_price)
if min_price == max_price:
return min_formatted
return f'{min_formatted} - {max_formatted}'
def truncate_text(text: str, max_length: int = 100, suffix: str = '...') -> str:
if len(text) <= max_length:
return text
return text[: max_length - len(suffix)] + suffix
def format_username(username: str | None, user_id: int, full_name: str | None = None) -> str:
if full_name:
return full_name
if username:
return f'@{username}'
return f'ID{user_id}'
def format_subscription_status(is_active: bool, is_trial: bool, end_date: datetime | str, language: str = 'ru') -> str:
if isinstance(end_date, str):
try:
end_date = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
except (ValueError, AttributeError):
end_date = datetime.now()
language_code = (language or 'ru').split('-')[0].lower()
use_russian_fallback = language_code in {'ru', 'fa'}
if not is_active:
return '❌ Неактивна' if use_russian_fallback else '❌ Inactive'
if is_trial:
status = '🎁 Тестовая' if use_russian_fallback else '🎁 Trial'
else:
status = '✅ Активна' if use_russian_fallback else '✅ Active'
now = datetime.utcnow()
if end_date > now:
days_left = (end_date - now).days
if days_left > 0:
status += f' ({days_left} дн.)' if use_russian_fallback else f' ({days_left} days)'
else:
hours_left = (end_date - now).seconds // 3600
status += f' ({hours_left} ч.)' if use_russian_fallback else f' ({hours_left} hrs)'
else:
status = '⏰ Истекла' if use_russian_fallback else '⏰ Expired'
return status
def format_traffic_usage(used_gb: float, limit_gb: int, language: str = 'ru') -> str:
language_code = (language or 'ru').split('-')[0].lower()
use_russian_fallback = language_code in {'ru', 'fa'}
if limit_gb == 0:
if use_russian_fallback:
return f'{used_gb:.1f} ГБ / ∞'
return f'{used_gb:.1f} GB / ∞'
percentage = (used_gb / limit_gb) * 100 if limit_gb > 0 else 0
if use_russian_fallback:
return f'{used_gb:.1f} ГБ / {limit_gb} ГБ ({percentage:.1f}%)'
return f'{used_gb:.1f} GB / {limit_gb} GB ({percentage:.1f}%)'
def format_boolean(value: bool, language: str = 'ru') -> str:
language_code = (language or 'ru').split('-')[0].lower()
if language_code in {'ru', 'fa'}:
return '✅ Да' if value else '❌ Нет'
return '✅ Yes' if value else '❌ No'