Coverage for src/secchi/workspace/aggregate.py: 94%
70 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 aggregation logic for multi-source and multi-project workspaces."""
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 logical_package_refs(refs: list[PackageRef]) -> list[PackageRef]:
19 """Collapse same-name registry refs for single-project navigation."""
20 grouped: dict[str, PackageRef] = {}
21 for ref in refs:
22 key = ref.name.lower()
23 current = grouped.get(key)
24 if current is None:
25 grouped[key] = replace(ref)
26 elif ref.favorite and not current.favorite:
27 current.favorite = True
28 return list(grouped.values())
31def combine_package_infos(ref: PackageRef, infos: list[PackageInfo]) -> PackageInfo:
32 """Combine the same logical package across supported registries."""
33 if not infos:
34 raise ValueError("at least one package info value is required")
36 primary = pick_primary_info(infos)
37 combined = replace(primary)
38 combined.name = ref.name
39 combined.source_registries = unique_registries(info.registry for info in infos)
40 combined.total_downloads = sum(info.total_downloads for info in infos)
41 combined.download_counts = replace(primary.download_counts)
42 combined.download_counts.today = sum(info.download_counts.today for info in infos)
43 combined.download_counts.week = sum(info.download_counts.week for info in infos)
44 combined.download_counts.month = sum(info.download_counts.month for info in infos)
45 combined.download_trend = combine_download_trends(infos)
47 best_github = next(
48 (info.github_stats for info in infos if info.github_stats.resolved), None
49 )
50 if best_github is not None:
51 combined.github_stats = best_github
53 crates_info = next(
54 (info for info in infos if info.registry is Registry.CRATES), None
55 )
56 if crates_info is not None:
57 combined.reverse_dependencies = crates_info.reverse_dependencies
58 combined.reverse_dependency_count = crates_info.reverse_dependency_count
59 combined.reverse_dependency_monthly_growth = (
60 crates_info.reverse_dependency_monthly_growth
61 )
63 combined.health_history = primary.health_history
64 return combined
67def pick_primary_info(infos: list[PackageInfo]) -> PackageInfo:
68 """Choose the preferred source for fields that cannot be combined."""
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 out: list[Registry] = []
79 for registry in registries:
80 if registry not in seen:
81 seen.add(registry)
82 out.append(registry)
83 return out
86def combine_download_trends(infos: list[PackageInfo]) -> list[DownloadTrendPoint]:
87 """Sum activity for equal periods across package registries."""
88 counts: dict[str, int] = {}
89 for info in infos:
90 for point in info.download_trend:
91 counts[point.date] = counts.get(point.date, 0) + point.count
92 return [
93 DownloadTrendPoint(date=date, count=counts[date]) for date in sorted(counts)
94 ]
97def combine_install_breakdown(infos: list[PackageInfo]) -> InstallBreakdown:
98 """Build ecosystem distribution from each source's best available total."""
99 totals: dict[str, int] = {}
100 for info in infos:
101 label = info.registry.display_name
102 count = info.download_counts.month or sum(
103 point.count for point in info.download_trend[-30:]
104 )
105 if count == 0:
106 count = info.total_downloads
107 totals[label] = totals.get(label, 0) + count
109 total = sum(totals.values())
110 if total <= 0:
111 return InstallBreakdown(
112 methods=[],
113 caption="No 30-day download data available across ecosystems.",
114 )
116 methods = [
117 InstallMethod(label=label, count=count, percent=count / total * 100)
118 for label, count in sorted(
119 totals.items(), key=lambda item: item[1], reverse=True
120 )
121 ]
122 return InstallBreakdown(
123 methods=methods,
124 caption="Combined from registry 30-day download totals.",
125 )