Coverage for src/secchi/renderers/summary.py: 89%
18 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-14 23:28 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-14 23:28 +0000
1"""Concise terminal rendering for ``secchi show``."""
3from __future__ import annotations
5from secchi.models import DerivedPackageData, PackageInfo
8def render_summary(info: PackageInfo, derived: DerivedPackageData) -> str:
9 change = derived.downloads_30d_pct_change
10 adoption = (
11 "—" if change is None else f"{'▲' if change >= 0 else '▼'} {abs(change):.1f}%"
12 )
13 stars = (
14 _format_count(info.github_stats.stars) if info.github_stats.resolved else "—"
15 )
16 dependents = _format_count(info.reverse_dependency_count)
17 advisories = str(len(info.security_advisories))
18 health = derived.health_score.total
19 return "\n".join(
20 [
21 info.name,
22 "─" * max(20, len(info.name)),
23 f"Health Score {health} / 100",
24 f"Latest Version {info.latest_version or '—'}",
25 f"Downloads {adoption}",
26 f"GitHub Stars {stars}",
27 f"Dependents {dependents}",
28 f"Security Advisories {advisories}",
29 ]
30 )
33def _format_count(value: int | None) -> str:
34 if value is None:
35 return "—"
36 if value >= 1_000_000:
37 return f"{value / 1_000_000:.1f}M"
38 if value >= 1_000:
39 return f"{value / 1_000:.1f}k"
40 return str(value)