Select Git revision
create-round
-
Martin Mareš authoredMartin Mareš authored
util_format.py 4.24 KiB
# Utils that do not depend on any other in mo (to avoid circular dependency)
from datetime import datetime
import decimal
from typing import Optional
import mo
def inflect_number(n: int, w1: str, w234: str, wother: str, unitprefix: str = '') -> str:
if n == 1:
return f'{n} {unitprefix}{w1}'
elif 2 <= n <= 4:
return f'{n} {unitprefix}{w234}'
else:
return f'{n} {unitprefix}{wother}'
def inflect_by_number(n: int, w1: str, w234: str, wother: str, unitprefix: str = '') -> str:
if n == 1:
return f'{unitprefix}{w1}'
elif 2 <= n <= 4:
return f'{unitprefix}{w234}'
else:
return f'{unitprefix}{wother}'
def inflect_with_number(n: int, w1: str, w234: str, wother: str) -> str:
if n == 1:
return w1 % n
elif 2 <= n <= 4:
return w234 % n
else:
return wother % n
def timeformat(dt: datetime) -> str:
if dt is None:
return '–'
else:
return dt.astimezone().strftime("%Y-%m-%d %H:%M")
def timeformat_short(dt: datetime) -> str:
if dt is None:
return '–'
else:
return dt.astimezone().strftime("%Y-%m-%d")
def timedelta(d: datetime, ref: Optional[datetime] = None, descriptive: bool = False) -> str:
"""Vyrábí česky formátované řetězece 'za 3 minuty', 'před 27 dny' a podobně
z rozdílu daného datetime a referenčního času (například now).
Pokud se předá `descriptive=True`, tak vyrábí "deskriptivní" popis
ve stylu '3 minuty po', '27 dní před' a podobně.
"""
if ref is None:
ref = mo.now
(prefix, suffix) = ("", "")
if ref > d:
delta = ref - d
if descriptive:
suffix = " před"
else:
prefix = "před "
before = True
else:
delta = d - ref
if descriptive:
suffix = " po"
else:
prefix = "za "
before = False
# Zkonstruujeme nejvýše dvě části: pokud je v první číslo >= 10, další už
# nekonstruujeme, pokud je <10, tak přidáme ještě jednu část. Tvoříme tedy
# řetězce jako: "za 28 dní", "za 8 dní 16 hodin", "za 7 hodin 55 minut",
# "za 55 minut", "za 3 minuty 12 sekund", ...
parts = []
prev = 0
if delta.days > 0:
if before:
parts.append(inflect_number(delta.days, 'den' if descriptive else 'dnem', 'dny', 'dny'))
else:
parts.append(inflect_number(delta.days, 'den', 'dny', 'dní'))
prev = delta.days
seconds = delta.seconds
for (unitprefix, s) in [('hodin', 3600), ('minut', 60), ('sekund', 1)]:
number = seconds // s
seconds = seconds % s
if len(parts) == 2 or (len(parts) == 1 and number == 0) or prev >= 10:
break
if number == 0:
continue
prev = number
if before:
# 1 minuta před, 3 minuty před, 5 minut před / před 1 minutou, před 3 minutami, před 5 minutami
if descriptive:
parts.append(inflect_number(number, 'a', 'y', '', unitprefix=unitprefix))
else:
parts.append(inflect_number(number, 'ou', 'ami', 'ami', unitprefix=unitprefix))
else:
# 1 minuta po, 3 minuty po, 5 minut po / za 1 minutu, za 3 minuty, za 5 minut
parts.append(inflect_number(number, 'a' if descriptive else 'u', 'y', '', unitprefix=unitprefix))
if len(parts) == 0:
if before and not descriptive:
parts.append("méně než sekundou")
else:
parts.append("méně než sekundu")
value = " ".join(parts)
return f"{prefix}{value}{suffix}"
def time_and_timedelta(d: datetime, ref: Optional[datetime] = None) -> str:
if d is None:
return '–'
return f"{timeformat(d)} ({timedelta(d, ref)})"
def data_size(bytes: int) -> str:
if bytes is None:
return '0 B'
elif bytes >= 1 << 30:
return f'{bytes/(1<<30):.1f} GiB'
elif bytes >= 1 << 20:
return f'{bytes/(1<<20):.1f} MiB'
else:
return f'{bytes/(1<<10):.1f} KiB'
def format_decimal(points: Optional[decimal.Decimal]) -> Optional[str]:
if points is None:
return None
elif points % 1 == 0:
return str(int(points))
else:
return str(points)