Coverage for src/secchi/aggregate.py: 20%
64 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-04 22:15 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-04 22:15 +0000
1"""Pure helpers for presenting one logical package across registries."""
3from __future__ import annotations
5from collections.abc import Iterable
6from dataclasses import replace
8from secchi.models import (
9 DownloadTrendPoint,
10 InstallBreakdown,
11 InstallMethod,
12 PackageInfo,
13 PackageRef,
14 Registry,
15)
18def package_key(ref: PackageRef) -> str:
19 project = f"{ref.project_name}:" if ref.project_name else ""
20 return f"{project}{ref.registry.value}:{ref.name}"
23def logical_package_refs(refs: list[PackageRef]) -> list[PackageRef]:
24 """Collapse same-named registry variants into one navigable package."""
25 grouped: dict[str, PackageRef] = {}
26 for ref in refs:
27 key = ref.name.lower()
28 current = grouped.get(key)
29 if current is None:
30 grouped[key] = replace(ref)
31 elif ref.favorite and not current.favorite:
32 current.favorite = True
33 return list(grouped.values())
36def combine_package_infos(ref: PackageRef, infos: list[PackageInfo]) -> PackageInfo:
37 """Combine compatible activity signals while retaining a primary profile."""
38 primary = pick_primary_info(infos)
39 combined = replace(primary)
40 combined.name = ref.name
41 combined.source_registries = unique_registries(info.registry for info in infos)
42 combined.total_downloads = sum(info.total_downloads for info in infos)
43 combined.download_counts = replace(primary.download_counts)
44 combined.download_counts.today = sum(info.download_counts.today for info in infos)
45 combined.download_counts.week = sum(info.download_counts.week for info in infos)
46 combined.download_counts.month = sum(info.download_counts.month for info in infos)
47 combined.download_trend = combine_download_trends(infos)
49 best_github = next(
50 (info.github_stats for info in infos if info.github_stats.resolved), None
51 )
52 if best_github is not None:
53 combined.github_stats = best_github
55 crates_info = next(
56 (info for info in infos if info.registry is Registry.CRATES), None
57 )
58 if crates_info is not None:
59 combined.reverse_dependencies = crates_info.reverse_dependencies
60 combined.reverse_dependency_count = crates_info.reverse_dependency_count
61 combined.reverse_dependency_monthly_growth = (
62 crates_info.reverse_dependency_monthly_growth
63 )
64 combined.health_history = primary.health_history
65 return combined
68def pick_primary_info(infos: list[PackageInfo]) -> PackageInfo:
69 for registry in (Registry.CRATES, Registry.PYPI, Registry.NPM):
70 for info in infos:
71 if info.registry is registry and info.latest_version:
72 return info
73 return infos[0]
76def unique_registries(registries: Iterable[Registry]) -> list[Registry]:
77 seen: set[Registry] = set()
78 return [
79 registry
80 for registry in registries
81 if not (registry in seen or seen.add(registry))
82 ]
85def combine_download_trends(infos: list[PackageInfo]) -> list[DownloadTrendPoint]:
86 counts: dict[str, int] = {}
87 for info in infos:
88 for point in info.download_trend:
89 counts[point.date] = counts.get(point.date, 0) + point.count
90 return [
91 DownloadTrendPoint(date=date, count=counts[date]) for date in sorted(counts)
92 ]
95def combine_install_breakdown(infos: list[PackageInfo]) -> InstallBreakdown:
96 totals: dict[str, int] = {}
97 for info in infos:
98 label = info.registry.display_name
99 count = info.download_counts.month or sum(
100 p.count for p in info.download_trend[-30:]
101 )
102 totals[label] = totals.get(label, 0) + (count or info.total_downloads)
103 total = sum(totals.values())
104 if total <= 0:
105 return InstallBreakdown(
106 caption="No 30-day download data available across ecosystems."
107 )
108 methods = [
109 InstallMethod(label=label, count=count, percent=count / total * 100)
110 for label, count in sorted(
111 totals.items(), key=lambda item: item[1], reverse=True
112 )
113 ]
114 return InstallBreakdown(
115 methods=methods, caption="Combined from registry 30-day download totals."
116 )