Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ pretty = true

[[tool.mypy.overrides]]
module = [
"cleo._utils",
"cleo.ui.table",
]
ignore_errors = true
Expand Down
66 changes: 36 additions & 30 deletions src/cleo/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import math

from dataclasses import dataclass
from html.parser import HTMLParser
from typing import Any

from rapidfuzz.distance import Levenshtein

Expand All @@ -13,31 +13,30 @@ def __init__(self) -> None:
super().__init__(convert_charrefs=False)

self.reset()
self.fed = []
self.fed: list[str] = []

def handle_data(self, d) -> None:
def handle_data(self, d: str) -> None:
self.fed.append(d)

def handle_entityref(self, name) -> None:
def handle_entityref(self, name: str) -> None:
self.fed.append(f"&{name};")

def handle_charref(self, name) -> None:
def handle_charref(self, name: str) -> None:
self.fed.append(f"&#{name};")

def get_data(self) -> str:
return "".join(self.fed)


def _strip(value) -> str:
def _strip(value: str) -> str:
s = TagStripper()
s.feed(value)
s.close()

return s.get_data()


def strip_tags(value: Any) -> str:
value = str(value)
def strip_tags(value: str) -> str:
while "<" in value and ">" in value:
new_value = _strip(value)
if value.count("<") == new_value.count("<"):
Expand Down Expand Up @@ -72,30 +71,37 @@ def find_similar_names(name: str, names: list[str]) -> list[str]:
distance_by_name = {
k: v for k, v in distance_by_name.items() if v[0] < 2 * threshold
}

# Display results with shortest distance first
return sorted(distance_by_name, key=distance_by_name.get)


_TIME_FORMATS = [
(0, "< 1 sec"),
(2, "1 sec"),
(59, "secs", 1),
(60, "1 min"),
(3600, "mins", 60),
(5400, "1 hr"),
(86400, "hrs", 3600),
(129600, "1 day"),
(604800, "days", 86400),
return sorted(distance_by_name, key=lambda x: distance_by_name[x])


@dataclass
class TimeFormat:
threshold: int
alias: str
divisor: int | None = None

def apply(self, secs: float) -> str:
if self.divisor:
return f"{math.ceil(secs / self.divisor)} {self.alias}"
return self.alias


_TIME_FORMATS: list[TimeFormat] = [
TimeFormat(1, "< 1 sec"),
TimeFormat(2, "1 sec"),
TimeFormat(60, "secs", 1),
TimeFormat(61, "1 min"),
TimeFormat(3600, "mins", 60),
TimeFormat(5401, "1 hr"),
TimeFormat(86400, "hrs", 3600),
TimeFormat(129601, "1 day"),
TimeFormat(604801, "days", 86400),
]


def format_time(secs: float) -> str:
for fmt in _TIME_FORMATS:
if secs > fmt[0]:
continue

if len(fmt) == 2:
return fmt[1]

return f"{math.ceil(secs / fmt[2])} {fmt[1]}"
format = next(
(fmt for fmt in _TIME_FORMATS if secs < fmt.threshold), _TIME_FORMATS[-1]
)
return format.apply(secs)
21 changes: 21 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,27 @@
import pytest

from cleo._utils import find_similar_names
from cleo._utils import format_time


@pytest.mark.parametrize(
["input_secs", "expected"],
[
(0.1, "< 1 sec"),
(1.0, "1 sec"),
(2.0, "2 secs"),
(59.0, "59 secs"),
(60.0, "1 min"),
(120.0, "2 mins"),
(3600.0, "1 hr"),
(7200.0, "2 hrs"),
(129600.0, "1 day"),
(129601.0, "2 days"),
(700000.0, "9 days"),
],
)
def test_format_time(input_secs: float, expected: str) -> None:
assert format_time(input_secs) == expected


@pytest.mark.parametrize(
Expand Down