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

1"""Concise terminal rendering for ``secchi show``.""" 

2 

3from __future__ import annotations 

4 

5from secchi.models import DerivedPackageData, PackageInfo 

6 

7 

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 ) 

31 

32 

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)