Coverage for app/venv/lib/python3.14/site-packages/weblate/trans/views/about.py: 44%
69 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
1# Copyright © Michal Čihař <michal@weblate.org>
2#
3# SPDX-License-Identifier: GPL-3.0-or-later
5import requests
6from django.core.cache import cache
7from django.db.models import Sum
8from django.utils.translation import gettext, gettext_lazy
9from django.views.generic import TemplateView
11from weblate.accounts.models import Profile
12from weblate.metrics.models import Metric
13from weblate.utils.requests import request
14from weblate.utils.requirements import get_versions_list
15from weblate.utils.stats import GlobalStats
16from weblate.vcs.gpg import get_gpg_public_key, get_gpg_sign_key
17from weblate.vcs.ssh import get_all_key_data
19MENU = (
20 ("index", "about", gettext_lazy("About Weblate")),
21 ("stats", "stats", gettext_lazy("Statistics")),
22 ("keys", "keys", gettext_lazy("Keys")),
23 ("donate", "donate", gettext_lazy("Support Weblate")),
24)
26REPO_URL = "https://api.github.com/repos/WeblateOrg/weblate"
27ACTIVITY_URL = "https://api.github.com/repos/WeblateOrg/weblate/stats/commit_activity"
28FALLBACK_STATS = {
29 "stars": 4082,
30 "issues": 447,
31 "commits": 663,
32}
35class AboutView(TemplateView):
36 page = "index"
38 def page_context(self, context) -> None:
39 context.update(
40 {
41 "title": gettext("About Weblate"),
42 "versions": get_versions_list(),
43 "allow_index": True,
44 }
45 )
47 def get_context_data(self, **kwargs):
48 context = super().get_context_data(**kwargs)
50 context["menu_items"] = MENU
51 context["menu_page"] = self.page
53 self.page_context(context)
55 return context
57 def get_template_names(self):
58 return [f"about/{self.page}.html"]
61class StatsView(AboutView):
62 page = "stats"
64 def page_context(self, context) -> None:
65 context["title"] = gettext("Weblate statistics")
67 stats = GlobalStats()
69 totals = Profile.objects.aggregate(Sum("translated"))
70 metrics = Metric.objects.get_current_metric(None, Metric.SCOPE_GLOBAL, 0)
72 context["total_translations"] = totals["translated__sum"]
73 context["stats"] = stats
74 context["metrics"] = metrics
76 context["top_users"] = top_users = (
77 Profile.objects.order_by("-translated")
78 .filter(user__is_bot=False, user__is_active=True)[:10]
79 .select_related("user")
80 )
81 translated_max = max(user.translated for user in top_users)
82 for user in top_users:
83 if translated_max:
84 user.translated_width = 100 * user.translated // translated_max
85 else:
86 user.translated_width = 0
89class KeysView(AboutView):
90 page = "keys"
92 def page_context(self, context) -> None:
93 context.update(
94 {
95 "title": gettext("Weblate keys"),
96 "gpg_key_id": get_gpg_sign_key(),
97 "gpg_key": get_gpg_public_key(),
98 "public_ssh_keys": get_all_key_data(),
99 "allow_index": True,
100 }
101 )
104class DonateView(AboutView):
105 page = "donate"
106 cache_key = "weblate-repo-stats"
108 def fetch_url(self, url: str):
109 response = request("get", url)
110 return response.json()
112 def get_stats(self) -> dict[str, int]:
113 result = cache.get(self.cache_key)
114 if result is None:
115 try:
116 repo = self.fetch_url(REPO_URL)
117 activity = self.fetch_url(ACTIVITY_URL)
118 except requests.exceptions.RequestException:
119 return FALLBACK_STATS
120 activity = sorted(activity, key=lambda item: -item["week"])
121 result = {
122 "stars": repo["stargazers_count"],
123 "issues": repo["open_issues_count"],
124 "commits": sum(item["total"] for item in activity[:8]),
125 }
126 cache.set(self.cache_key, result, 24 * 3600)
127 return result
129 def page_context(self, context) -> None:
130 context["title"] = gettext("Support Weblate")
131 context.update(self.get_stats())