diff --git a/pontoon/administration/templates/admin.html b/pontoon/administration/templates/admin.html index b771f4308..b9a49539a 100644 --- a/pontoon/administration/templates/admin.html +++ b/pontoon/administration/templates/admin.html @@ -27,7 +27,7 @@ {% set main_link = url('pontoon.admin.project', project.slug) %} {% set chart_link = url('pontoon.admin.project', project.slug) %} {% set latest_activity = project.get_latest_activity() %} - {% set chart = project.get_chart() %} + {% set chart = project_stats.get(project.id, {'total': 0}) %} {{ ProjectList.item(project, main_link, chart_link, latest_activity, chart) }} {% endfor %} diff --git a/pontoon/administration/views.py b/pontoon/administration/views.py index ae34bb48c..32657ac6f 100644 --- a/pontoon/administration/views.py +++ b/pontoon/administration/views.py @@ -49,7 +49,15 @@ def admin(request): .order_by("name") ) - return render(request, "admin.html", {"admin": True, "projects": projects}) + return render( + request, + "admin.html", + { + "admin": True, + "projects": projects, + "project_stats": projects.stats_data(), + }, + ) @login_required(redirect_field_name="", login_url="/403") diff --git a/pontoon/base/aggregated_stats.py b/pontoon/base/aggregated_stats.py index de1c9424e..8c788a089 100644 --- a/pontoon/base/aggregated_stats.py +++ b/pontoon/base/aggregated_stats.py @@ -1,5 +1,3 @@ -import math - from functools import cached_property @@ -42,7 +40,7 @@ def unreviewed_strings(self) -> int: return self._stats["unreviewed"] @property - def missing_strings(self): + def missing_strings(self) -> int: return ( self.total_strings - self.approved_strings @@ -51,42 +49,42 @@ def missing_strings(self): - self.strings_with_warnings ) + @property + def complete(self) -> bool: + return ( + self.total_strings + == self.approved_strings + + self.pretranslated_strings + + self.strings_with_warnings + ) + -def get_completed_percent(obj): - if not obj.total_strings: - return 0 - completed_strings = ( - obj.approved_strings + obj.pretranslated_strings + obj.strings_with_warnings - ) - return completed_strings / obj.total_strings * 100 - - -def get_chart_dict(obj: "AggregatedStats"): - """Get chart data dictionary""" - if ts := obj.total_strings: - return { - "total": ts, - "approved": obj.approved_strings, - "pretranslated": obj.pretranslated_strings, - "errors": obj.strings_with_errors, - "warnings": obj.strings_with_warnings, - "unreviewed": obj.unreviewed_strings, - "approved_share": round(obj.approved_strings / ts * 100), - "pretranslated_share": round(obj.pretranslated_strings / ts * 100), - "errors_share": round(obj.strings_with_errors / ts * 100), - "warnings_share": round(obj.strings_with_warnings / ts * 100), - "unreviewed_share": round(obj.unreviewed_strings / ts * 100), - "completion_percent": int(math.floor(get_completed_percent(obj))), - } - - -def get_top_instances(qs): +def get_top_instances(qs, stats: dict[int, dict[str, int]]) -> dict[str, object] | None: """ Get top instances in the queryset. """ + + if not stats: + return None + + def _missing(x: tuple[int, dict[str, int]]) -> int: + _, d = x + return ( + d["total"] + - d["approved"] + - d["pretranslated"] + - d["errors"] + - d["warnings"] + ) + + max_total_id = max(stats.items(), key=lambda x: x[1]["total"])[0] + max_approved_id = max(stats.items(), key=lambda x: x[1]["approved"])[0] + max_suggestions_id = max(stats.items(), key=lambda x: x[1]["unreviewed"])[0] + max_missing_id = max(stats.items(), key=_missing)[0] + return { - "most_strings": sorted(qs, key=lambda x: x.total_strings)[-1], - "most_translations": sorted(qs, key=lambda x: x.approved_strings)[-1], - "most_suggestions": sorted(qs, key=lambda x: x.unreviewed_strings)[-1], - "most_missing": sorted(qs, key=lambda x: x.missing_strings)[-1], + "most_strings": next(row for row in qs if row.id == max_total_id), + "most_translations": next(row for row in qs if row.id == max_approved_id), + "most_suggestions": next(row for row in qs if row.id == max_suggestions_id), + "most_missing": next(row for row in qs if row.id == max_missing_id), } diff --git a/pontoon/base/models/locale.py b/pontoon/base/models/locale.py index 73aaae2fe..83d8dfa01 100644 --- a/pontoon/base/models/locale.py +++ b/pontoon/base/models/locale.py @@ -7,7 +7,7 @@ from django.contrib.auth.models import Group from django.core.exceptions import ValidationError from django.db import models -from django.db.models import Prefetch +from django.db.models import Prefetch, Sum from pontoon.base.aggregated_stats import AggregatedStats @@ -79,6 +79,34 @@ def prefetch_project_locale(self, project): ) ) + def stats_data(self, project=None) -> dict[int, dict[str, int]]: + """Mapping of locale `id` to dict with counts.""" + if project is not None: + query = self.filter(translatedresources__resource__project=project) + else: + query = self.filter( + translatedresources__resource__project__disabled=False, + translatedresources__resource__project__system_project=False, + translatedresources__resource__project__visibility="public", + ) + data = query.annotate( + total=Sum("translatedresources__total_strings", default=0), + approved=Sum("translatedresources__approved_strings", default=0), + pretranslated=Sum("translatedresources__pretranslated_strings", default=0), + errors=Sum("translatedresources__strings_with_errors", default=0), + warnings=Sum("translatedresources__strings_with_warnings", default=0), + unreviewed=Sum("translatedresources__unreviewed_strings", default=0), + ).values( + "id", + "total", + "approved", + "pretranslated", + "errors", + "warnings", + "unreviewed", + ) + return {row["id"]: row for row in data if row["total"]} + class Locale(models.Model, AggregatedStats): @property @@ -366,11 +394,6 @@ def get_latest_activity(self, project=None): return ProjectLocale.get_latest_activity(self, project) - def get_chart(self, project=None): - from pontoon.base.models.project_locale import ProjectLocale - - return ProjectLocale.get_chart(self, project) - def save(self, *args, **kwargs): old = Locale.objects.get(pk=self.pk) if self.pk else None super().save(*args, **kwargs) diff --git a/pontoon/base/models/project.py b/pontoon/base/models/project.py index cffdbf53a..32465c9f2 100644 --- a/pontoon/base/models/project.py +++ b/pontoon/base/models/project.py @@ -4,7 +4,7 @@ from django.conf import settings from django.contrib.auth.models import User from django.db import models -from django.db.models import Prefetch +from django.db.models import Prefetch, Sum from django.db.models.manager import BaseManager from django.utils import timezone @@ -86,6 +86,32 @@ def prefetch_project_locale(self, locale): ) ) + def stats_data(self, locale=None) -> dict[int, dict[str, int]]: + """Mapping of project `id` to dict with counts.""" + query = ( + self + if locale is None + else self.filter(resources__translatedresources__locale=locale) + ) + tr = "resources__translatedresources" + data = query.annotate( + total=Sum(f"{tr}__total_strings", default=0), + approved=Sum(f"{tr}__approved_strings", default=0), + pretranslated=Sum(f"{tr}__pretranslated_strings", default=0), + errors=Sum(f"{tr}__strings_with_errors", default=0), + warnings=Sum(f"{tr}__strings_with_warnings", default=0), + unreviewed=Sum(f"{tr}__unreviewed_strings", default=0), + ).values( + "id", + "total", + "approved", + "pretranslated", + "errors", + "warnings", + "unreviewed", + ) + return {row["id"]: row for row in data if row["total"]} + class Project(models.Model, AggregatedStats): @property @@ -252,11 +278,6 @@ def get_latest_activity(self, locale=None): return ProjectLocale.get_latest_activity(self, locale) - def get_chart(self, locale=None): - from pontoon.base.models.project_locale import ProjectLocale - - return ProjectLocale.get_chart(self, locale) - @property def avg_string_count(self): return int(self.total_strings / self.enabled_locales) diff --git a/pontoon/base/models/project_locale.py b/pontoon/base/models/project_locale.py index 36ceab48e..40496d2ef 100644 --- a/pontoon/base/models/project_locale.py +++ b/pontoon/base/models/project_locale.py @@ -2,7 +2,7 @@ from django.db import models from pontoon.base import utils -from pontoon.base.aggregated_stats import AggregatedStats, get_chart_dict +from pontoon.base.aggregated_stats import AggregatedStats from pontoon.base.models.locale import Locale from pontoon.base.models.project import Project @@ -111,34 +111,3 @@ def get_latest_activity(cls, self, extra=None): latest_translation = project_locale.latest_translation return latest_translation.latest_activity if latest_translation else None - - @classmethod - def get_chart(cls, self, extra=None): - """ - Get chart for project, locale or combination of both. - - :param self: object to get data for, - instance of Project or Locale - :param extra: extra filter to be used, - instance of Project or Locale - """ - chart = None - - if getattr(self, "fetched_project_locale", None): - if self.fetched_project_locale: - chart = get_chart_dict(self.fetched_project_locale[0]) - - elif extra is None: - chart = get_chart_dict(self) - - else: - project = self if isinstance(self, Project) else extra - locale = self if isinstance(self, Locale) else extra - project_locale = utils.get_object_or_none( - ProjectLocale, project=project, locale=locale - ) - - if project_locale is not None: - chart = get_chart_dict(project_locale) - - return chart diff --git a/pontoon/base/models/translated_resource.py b/pontoon/base/models/translated_resource.py index 0f82cbc3c..ef10712b0 100644 --- a/pontoon/base/models/translated_resource.py +++ b/pontoon/base/models/translated_resource.py @@ -113,6 +113,16 @@ def count_total_strings(self): total += (self.locale.nplurals - 1) * plural_count return total + def stats_data(self) -> dict[str, int]: + return { + "total": self.total_strings, + "approved": self.approved_strings, + "pretranslated": self.pretranslated_strings, + "errors": self.strings_with_errors, + "warnings": self.strings_with_warnings, + "unreviewed": self.unreviewed_strings, + } + def adjust_stats( self, before: dict[str, int], after: dict[str, int], tr_created: bool ): diff --git a/pontoon/base/templates/widgets/progress_chart.html b/pontoon/base/templates/widgets/progress_chart.html index 8a0dc33ac..401e3adb2 100644 --- a/pontoon/base/templates/widgets/progress_chart.html +++ b/pontoon/base/templates/widgets/progress_chart.html @@ -1,13 +1,13 @@ {% macro span(chart, link, link_parameter=False, has_params=False) %} {% if chart != None %}
- {{ chart.completion_percent }}% + {{ (100 * (chart.approved + chart.pretranslated + chart.warnings) / chart.total) | round(0, 'floor') | int }}% - - - - - + + + + +
diff --git a/pontoon/insights/tasks.py b/pontoon/insights/tasks.py index ca3056cd5..f32416f1c 100644 --- a/pontoon/insights/tasks.py +++ b/pontoon/insights/tasks.py @@ -12,7 +12,6 @@ from django.utils import timezone from pontoon.actionlog.models import ActionLog -from pontoon.base.aggregated_stats import get_completed_percent from pontoon.base.models import ( Entity, Locale, @@ -216,7 +215,14 @@ def get_locale_insights_snapshot( entities_count, ): """Create LocaleInsightsSnapshot instance for the given locale and day using given data.""" - locale_stats = TranslatedResource.objects.filter(locale=locale).string_stats() + lc_stats = TranslatedResource.objects.filter(locale=locale).string_stats() + lc_completion = ( + 100 + * (lc_stats["approved"] + lc_stats["pretranslated"] + lc_stats["warnings"]) + / lc_stats["total"] + if lc_stats["total"] > 0 + else 0 + ) all_managers, all_reviewers = get_privileged_users_data(privileged_users) all_contributors = {c["user"] for c in contributors} @@ -286,12 +292,12 @@ def get_locale_insights_snapshot( locale=locale, created_at=start_of_today, # Aggregated stats - total_strings=locale_stats["total"], - approved_strings=locale_stats["approved"], - pretranslated_strings=locale_stats["pretranslated"], - strings_with_errors=locale_stats["errors"], - strings_with_warnings=locale_stats["warnings"], - unreviewed_strings=locale_stats["unreviewed"], + total_strings=lc_stats["total"], + approved_strings=lc_stats["approved"], + pretranslated_strings=lc_stats["pretranslated"], + strings_with_errors=lc_stats["errors"], + strings_with_warnings=lc_stats["warnings"], + unreviewed_strings=lc_stats["unreviewed"], # Active users total_managers=total_managers, total_reviewers=total_reviewers, @@ -307,7 +313,7 @@ def get_locale_insights_snapshot( # Time to review pretranslations time_to_review_pretranslations=time_to_review_pretranslations, # Translation activity - completion=round(get_completed_percent(locale), 2), + completion=round(lc_completion, 2), human_translations=human_translations, machinery_translations=machinery_translations, new_source_strings=entities_count, @@ -334,6 +340,13 @@ def get_project_locale_insights_snapshot( pl_stats = TranslatedResource.objects.filter( locale=project_locale.locale, resource__project=project_locale.project ).string_stats() + pl_completion = ( + 100 + * (pl_stats["approved"] + pl_stats["pretranslated"] + pl_stats["warnings"]) + / pl_stats["total"] + if pl_stats["total"] > 0 + else 0 + ) ( human_translations, @@ -361,7 +374,7 @@ def get_project_locale_insights_snapshot( strings_with_warnings=pl_stats["warnings"], unreviewed_strings=pl_stats["unreviewed"], # Translation activity - completion=round(get_completed_percent(project_locale), 2), + completion=round(pl_completion, 2), human_translations=human_translations, machinery_translations=machinery_translations, new_source_strings=entities_count, diff --git a/pontoon/localizations/templates/localizations/widgets/resource_list.html b/pontoon/localizations/templates/localizations/widgets/resource_list.html index b2d7d0f40..bb677cb66 100644 --- a/pontoon/localizations/templates/localizations/widgets/resource_list.html +++ b/pontoon/localizations/templates/localizations/widgets/resource_list.html @@ -35,7 +35,7 @@

{% if deadline %} - {{ Deadline.deadline(resource.deadline, chart.completion_percent == 100) }} + {{ Deadline.deadline(resource.deadline, chart.total > 0 and chart.total == chart.approved + chart.pretranslated + chart.warnings) }} {% endif %} diff --git a/pontoon/localizations/tests/test_views.py b/pontoon/localizations/tests/test_views.py index cc5f77fe9..f002775c7 100644 --- a/pontoon/localizations/tests/test_views.py +++ b/pontoon/localizations/tests/test_views.py @@ -57,10 +57,4 @@ def test_ajax_resources(mock_render, client, project_a, locale_a): "unreviewed": 0, "errors": 0, "warnings": 0, - "approved_share": 0.0, - "unreviewed_share": 0.0, - "pretranslated_share": 0.0, - "warnings_share": 0.0, - "errors_share": 0.0, - "completion_percent": 0, } diff --git a/pontoon/localizations/views.py b/pontoon/localizations/views.py index 6861e8128..65ae7bcac 100644 --- a/pontoon/localizations/views.py +++ b/pontoon/localizations/views.py @@ -6,7 +6,6 @@ from django.shortcuts import get_object_or_404, render from django.views.generic.detail import DetailView -from pontoon.base.aggregated_stats import get_chart_dict from pontoon.base.models import ( Locale, Project, @@ -100,7 +99,7 @@ def ajax_resources(request, code, slug): tr.latest_activity = ( tr.latest_translation.latest_activity if tr.latest_translation else None ) - tr.chart = get_chart_dict(tr) + tr.chart = tr.stats_data() return render( request, diff --git a/pontoon/projects/templates/projects/includes/teams.html b/pontoon/projects/templates/projects/includes/teams.html index 2b30c236c..56ecc80fa 100644 --- a/pontoon/projects/templates/projects/includes/teams.html +++ b/pontoon/projects/templates/projects/includes/teams.html @@ -19,11 +19,12 @@ {% set locale_projects = project.available_locales_list() %} {% for locale in locales %} - {% if not tag %} {% set main_link = url('pontoon.projects.project', project.slug) %} {% set chart_link = url('pontoon.translate', locale.code, project.slug, 'all-resources') %} - {% set latest_activity = locale.get_latest_activity(project) %} - {% set chart = locale.get_chart(project) %} + + {% if not tag %} + {% set latest_activity = locale.get_latest_activity(project) %} + {% set chart = locale_stats.get(locale.id, {'total': 0}) %} {% endif %} {% if locale.code in locale_projects %} {% set class = 'limited' %} @@ -35,8 +36,8 @@ {% endif %} {% if tag %} - {% set main_link = url('pontoon.projects.project', project.slug) + '?tag=' + tag.slug %} - {% set chart_link = url('pontoon.translate', locale.code, project.slug, 'all-resources') + '?tag=' + tag.slug %} + {% set main_link = main_link + '?tag=' + tag.slug %} + {% set chart_link = chart_link + '?tag=' + tag.slug %} {% set latest_activity = locale.latest_activity %} {% set chart = locale.chart %} {% if chart %} diff --git a/pontoon/projects/templates/projects/projects.html b/pontoon/projects/templates/projects/projects.html index c219794c7..5b4da8963 100644 --- a/pontoon/projects/templates/projects/projects.html +++ b/pontoon/projects/templates/projects/projects.html @@ -22,34 +22,36 @@

class='projects-count', value=projects|length) }} - {{ HeadingInfo.details_item( - title='Most translations', - class='most-translations', - value=top_instances['most_translations'], - value_link=url('pontoon.projects.project', top_instances['most_translations'].slug)) - }} - {{ HeadingInfo.details_item( - title='Most unreviewed', - class='most-suggestions', - value=top_instances['most_suggestions'], - value_link=url('pontoon.projects.project', top_instances['most_suggestions'].slug)) - }} - {{ HeadingInfo.details_item( - title='Most missing strings', - class='missing-strings', - value=top_instances['most_missing'], - value_link=url('pontoon.projects.project', top_instances['most_missing'].slug)) - }} - {{ HeadingInfo.details_item( - title='Most enabled strings', - class='most-strings', - value=top_instances['most_strings'], - value_link=url('pontoon.projects.project', top_instances['most_strings'].slug)) - }} + {% if top_instances %} + {{ HeadingInfo.details_item( + title='Most translations', + class='most-translations', + value=top_instances['most_translations'], + value_link=url('pontoon.projects.project', top_instances['most_translations'].slug)) + }} + {{ HeadingInfo.details_item( + title='Most unreviewed', + class='most-suggestions', + value=top_instances['most_suggestions'], + value_link=url('pontoon.projects.project', top_instances['most_suggestions'].slug)) + }} + {{ HeadingInfo.details_item( + title='Most missing strings', + class='missing-strings', + value=top_instances['most_missing'], + value_link=url('pontoon.projects.project', top_instances['most_missing'].slug)) + }} + {{ HeadingInfo.details_item( + title='Most enabled strings', + class='most-strings', + value=top_instances['most_strings'], + value_link=url('pontoon.projects.project', top_instances['most_strings'].slug)) + }} + {% endif %} {{ HeadingInfo.progress_chart() }} - {{ HeadingInfo.progress_chart_legend(project_stats) }} + {{ HeadingInfo.progress_chart_legend(all_projects_stats) }} {% endblock %} @@ -70,7 +72,7 @@

{% set main_link = url('pontoon.projects.project', project.slug) %} {% set chart_link = url('pontoon.projects.project', project.slug) %} {% set latest_activity = project.get_latest_activity() %} - {% set chart = project.get_chart() %} + {% set chart = project_stats.get(project.id, {'total': 0}) %} {{ ProjectList.item(project, main_link, chart_link, latest_activity, chart) }} {% endfor %} diff --git a/pontoon/projects/templates/projects/widgets/project_list.html b/pontoon/projects/templates/projects/widgets/project_list.html index 8ab9337c5..c58bd2dd7 100644 --- a/pontoon/projects/templates/projects/widgets/project_list.html +++ b/pontoon/projects/templates/projects/widgets/project_list.html @@ -30,7 +30,7 @@

- {{ Deadline.deadline(project.deadline, chart.completion_percent == 100) }} + {{ Deadline.deadline(project.deadline, chart.total > 0 and chart.total == chart.approved + chart.pretranslated + chart.warnings) }} {{ Priority.priority(project.priority) }} diff --git a/pontoon/projects/views.py b/pontoon/projects/views.py index 522a469e9..80aaea315 100644 --- a/pontoon/projects/views.py +++ b/pontoon/projects/views.py @@ -28,15 +28,17 @@ def projects(request): if not projects: return render(request, "no_projects.html", {"title": "Projects"}) + project_stats = projects.stats_data() return render( request, "projects/projects.html", { "projects": projects, - "project_stats": TranslatedResource.objects.all().string_stats( + "all_projects_stats": TranslatedResource.objects.all().string_stats( request.user ), - "top_instances": get_top_instances(projects), + "project_stats": project_stats, + "top_instances": get_top_instances(projects, project_stats), }, ) @@ -77,7 +79,7 @@ def project(request, slug): @require_AJAX def ajax_teams(request, slug): - """Teams tab.""" + """Project Teams tab.""" project = get_object_or_404( Project.objects.visible_for(request.user).available(), slug=slug ) @@ -95,13 +97,17 @@ def ajax_teams(request, slug): return render( request, "projects/includes/teams.html", - {"project": project, "locales": locales}, + { + "project": project, + "locales": locales, + "locale_stats": locales.stats_data(project), + }, ) @require_AJAX def ajax_tags(request, slug): - """Tags tab.""" + """Project Tags tab.""" project = get_object_or_404(Project.objects.visible_for(request.user), slug=slug) if not project.tags_enabled: @@ -118,7 +124,7 @@ def ajax_tags(request, slug): @require_AJAX def ajax_insights(request, slug): - """Insights tab.""" + """Project Insights tab.""" if not settings.ENABLE_INSIGHTS: raise ImproperlyConfigured("ENABLE_INSIGHTS variable not set in settings.") @@ -139,7 +145,7 @@ def ajax_insights(request, slug): @require_AJAX def ajax_info(request, slug): - """Info tab.""" + """Project Info tab.""" project = get_object_or_404( Project.objects.visible_for(request.user).available(), slug=slug ) diff --git a/pontoon/tags/tests/test_utils.py b/pontoon/tags/tests/test_utils.py index 72a58e82d..ca866f9f4 100644 --- a/pontoon/tags/tests/test_utils.py +++ b/pontoon/tags/tests/test_utils.py @@ -3,24 +3,6 @@ from pontoon.tags.utils import Tags -@pytest.fixture -def chart_0(): - return { - "total": 0, - "approved": 0, - "pretranslated": 0, - "errors": 0, - "warnings": 0, - "unreviewed": 0, - "approved_share": 0, - "pretranslated_share": 0, - "errors_share": 0, - "warnings_share": 0, - "unreviewed_share": 0, - "completion_percent": 0, - } - - @pytest.mark.django_db def test_tags_get_no_project(): tags = Tags().get() @@ -28,12 +10,7 @@ def test_tags_get_no_project(): @pytest.mark.django_db -def test_tags_get( - chart_0, - project_a, - tag_a, - translation_a, -): +def test_tags_get(project_a, tag_a, translation_a): tags = Tags(project=project_a).get() tag = tags[0] @@ -42,20 +19,19 @@ def test_tags_get( assert tag.priority == tag_a.priority assert tag.latest_activity == translation_a.latest_activity - chart = chart_0 - chart["total"] = 1 - chart["unreviewed"] = 1 - chart["unreviewed_share"] = 100.0 - assert tag.chart == chart + assert tag.chart == { + "total": 1, + "approved": 0, + "pretranslated": 0, + "errors": 0, + "warnings": 0, + "unreviewed": 1, + "resource__tag": tag.id, + } @pytest.mark.django_db -def test_tags_get_tag_locales( - chart_0, - project_a, - project_locale_a, - tag_a, -): +def test_tags_get_tag_locales(project_a, project_locale_a, tag_a): tags = Tags(project=project_a, slug=tag_a.slug) tag = tags.get_tag_locales() @@ -67,10 +43,24 @@ def test_tags_get_tag_locales( with pytest.raises(AttributeError): tag.latest_activity - chart = chart_0 - chart["total"] = 1 - assert tag.chart == chart + assert tag.chart == { + "total": 1, + "approved": 0, + "pretranslated": 0, + "errors": 0, + "warnings": 0, + "unreviewed": 0, + "resource__tag": tag.id, + } locale = tag.locales.first() assert locale.latest_activity is None - assert locale.chart == chart + assert locale.chart == { + "total": 1, + "approved": 0, + "pretranslated": 0, + "errors": 0, + "warnings": 0, + "unreviewed": 0, + "locale": locale.id, + } diff --git a/pontoon/tags/utils.py b/pontoon/tags/utils.py index 5eb793f7e..ed6bf5f82 100644 --- a/pontoon/tags/utils.py +++ b/pontoon/tags/utils.py @@ -1,6 +1,5 @@ from django.db.models import Max, Q, Sum -from pontoon.base.aggregated_stats import get_chart_dict from pontoon.base.models import TranslatedResource, Translation from pontoon.tags.models import Tag @@ -67,21 +66,7 @@ def chart(self, query, group_by): unreviewed=Sum("unreviewed_strings"), ) ) - - print(list(k for k in trs[0].keys())) - return { - tr[group_by]: get_chart_dict( - TranslatedResource( - total_strings=tr["total"], - approved_strings=tr["approved"], - pretranslated_strings=tr["pretranslated"], - strings_with_errors=tr["errors"], - strings_with_warnings=tr["warnings"], - unreviewed_strings=tr["unreviewed"], - ) - ) - for tr in trs - } + return {tr[group_by]: tr for tr in trs} def latest_activity(self, query, group_by): latest_activity = {} diff --git a/pontoon/teams/templates/teams/includes/projects.html b/pontoon/teams/templates/teams/includes/projects.html index 90fa21cfd..06514890f 100644 --- a/pontoon/teams/templates/teams/includes/projects.html +++ b/pontoon/teams/templates/teams/includes/projects.html @@ -26,7 +26,7 @@ {% set main_link = url('pontoon.projects.project', project.slug) %} {% set chart_link = url('pontoon.translate', locale.code, project.slug, 'all-resources') %} {% set latest_activity = project.get_latest_activity(locale) %} - {% set chart = project.get_chart(locale) %} + {% set chart = project_stats.get(project.id, {'total': 0}) %} {% if project in enabled_projects %} {% set class = 'limited' %} {% if project in pretranslated_projects %} diff --git a/pontoon/teams/templates/teams/teams.html b/pontoon/teams/templates/teams/teams.html index 0334bf000..e52fb9ae4 100644 --- a/pontoon/teams/templates/teams/teams.html +++ b/pontoon/teams/templates/teams/teams.html @@ -22,34 +22,36 @@

class='teams-count', value=locales|length) }} - {{ HeadingInfo.details_item( - title='Most translations', - class='most-translations', - value=top_instances['most_translations'], - value_link=url('pontoon.teams.team', top_instances['most_translations'].code)) - }} - {{ HeadingInfo.details_item( - title='Most unreviewed', - class='most-suggestions', - value=top_instances['most_suggestions'], - value_link=url('pontoon.teams.team', top_instances['most_suggestions'].code)) - }} - {{ HeadingInfo.details_item( - title='Most missing strings', - class='missing-strings', - value=top_instances['most_missing'], - value_link=url('pontoon.teams.team', top_instances['most_missing'].code)) - }} - {{ HeadingInfo.details_item( - title='Most enabled strings', - class='most-strings', - value=top_instances['most_strings'], - value_link=url('pontoon.teams.team', top_instances['most_strings'].code)) - }} + {% if top_instances %} + {{ HeadingInfo.details_item( + title='Most translations', + class='most-translations', + value=top_instances['most_translations'], + value_link=url('pontoon.teams.team', top_instances['most_translations'].code)) + }} + {{ HeadingInfo.details_item( + title='Most unreviewed', + class='most-suggestions', + value=top_instances['most_suggestions'], + value_link=url('pontoon.teams.team', top_instances['most_suggestions'].code)) + }} + {{ HeadingInfo.details_item( + title='Most missing strings', + class='missing-strings', + value=top_instances['most_missing'], + value_link=url('pontoon.teams.team', top_instances['most_missing'].code)) + }} + {{ HeadingInfo.details_item( + title='Most enabled strings', + class='most-strings', + value=top_instances['most_strings'], + value_link=url('pontoon.teams.team', top_instances['most_strings'].code)) + }} + {% endif %} {{ HeadingInfo.progress_chart() }} - {{ HeadingInfo.progress_chart_legend(locale_stats) }} + {{ HeadingInfo.progress_chart_legend(all_locales_stats) }} {% endblock %} @@ -76,7 +78,7 @@

{% set main_link = url('pontoon.teams.team', locale.code) %} {% set chart_link = url('pontoon.translate', locale.code, 'all-projects', 'all-resources') %} {% set latest_activity = locale.get_latest_activity() %} - {% set chart = locale.get_chart() %} + {% set chart = locale_stats.get(locale.id, {'total': 0}) %} {{ TeamList.item(locale, main_link, chart_link, latest_activity, chart, link_parameter=True, not_ready_message='No projects enabled yet') }} {% endfor %} diff --git a/pontoon/teams/views.py b/pontoon/teams/views.py index e9f38ee5d..13b440db9 100644 --- a/pontoon/teams/views.py +++ b/pontoon/teams/views.py @@ -61,14 +61,16 @@ def teams(request): if not locales: return render(request, "no_projects.html", {"title": "Teams"}) + locale_stats = locales.stats_data() return render( request, "teams/teams.html", { "locales": locales, - "locale_stats": TranslatedResource.objects.all().string_stats(request.user), + "all_locales_stats": TranslatedResource.objects.all().string_stats(), + "locale_stats": locale_stats, "form": form, - "top_instances": get_top_instances(locales), + "top_instances": get_top_instances(locales, locale_stats), }, ) @@ -97,7 +99,7 @@ def team(request, locale): @require_AJAX def ajax_projects(request, locale): - """Projects tab.""" + """Team Projects tab.""" locale = get_object_or_404(Locale, code=locale) projects = ( @@ -136,6 +138,7 @@ def ajax_projects(request, locale): { "locale": locale, "projects": projects, + "project_stats": Project.objects.visible().stats_data(locale), "enabled_projects": enabled_projects, "no_visible_projects": no_visible_projects, "project_request_enabled": project_request_enabled, @@ -147,7 +150,7 @@ def ajax_projects(request, locale): @require_AJAX def ajax_insights(request, locale): - """Insights tab.""" + """Team Insights tab.""" if not settings.ENABLE_INSIGHTS: raise ImproperlyConfigured("ENABLE_INSIGHTS variable not set in settings.") @@ -166,7 +169,7 @@ def ajax_insights(request, locale): @require_AJAX def ajax_info(request, locale): - """Info tab.""" + """Team Info tab.""" locale = get_object_or_404(Locale, code=locale) return render(request, "teams/includes/info.html", {"locale": locale})