From d077b211d1228aa058c92daa780557cc39413e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matja=C5=BE=20Horvat?= Date: Wed, 21 Feb 2024 14:02:43 +0100 Subject: [PATCH] Optimize logic to retrieve Resources in the Localization dashboard --- .../localizations/includes/resources.html | 12 +- .../localizations/widgets/resource_list.html | 4 +- pontoon/localizations/tests/test_views.py | 118 +++++------------- pontoon/localizations/views.py | 93 +++++--------- 4 files changed, 71 insertions(+), 156 deletions(-) diff --git a/pontoon/localizations/templates/localizations/includes/resources.html b/pontoon/localizations/templates/localizations/includes/resources.html index 23237521e0..038fe904dc 100644 --- a/pontoon/localizations/templates/localizations/includes/resources.html +++ b/pontoon/localizations/templates/localizations/includes/resources.html @@ -11,13 +11,11 @@ {{ ResourceList.header(deadline, priority) }} {% for resource in resources %} - {% if not loop.last %} - {% set main_link = url('pontoon.translate', locale.code, project.slug, resource.title) %} - {% set chart_link = main_link %} - {% set latest_activity = resource.latest_activity %} - {% set chart = resource.chart %} - {{ ResourceList.item(resource, main_link, chart_link, latest_activity, chart, deadline, priority) }} - {% endif %} + {% set main_link = url('pontoon.translate', locale.code, project.slug, resource.title) %} + {% set chart_link = main_link %} + {% set latest_activity = resource.latest_activity %} + {% set chart = resource.chart %} + {{ ResourceList.item(resource, main_link, chart_link, latest_activity, chart, deadline, priority) }} {% endfor %} {{ ResourceList.footer() }} diff --git a/pontoon/localizations/templates/localizations/widgets/resource_list.html b/pontoon/localizations/templates/localizations/widgets/resource_list.html index a9b932c0d4..867d19b219 100644 --- a/pontoon/localizations/templates/localizations/widgets/resource_list.html +++ b/pontoon/localizations/templates/localizations/widgets/resource_list.html @@ -35,13 +35,13 @@

{% if deadline %} - {{ Deadline.deadline(resource.resource__deadline, chart.completion_percent == 100) }} + {{ Deadline.deadline(resource.deadline, chart.completion_percent == 100) }} {% endif %} {% if priority %} - {{ Priority.priority(resource.resource__priority) }} + {{ Priority.priority(resource.priority) }} {% endif %} diff --git a/pontoon/localizations/tests/test_views.py b/pontoon/localizations/tests/test_views.py index 124ed97ca9..9d65a30571 100644 --- a/pontoon/localizations/tests/test_views.py +++ b/pontoon/localizations/tests/test_views.py @@ -5,8 +5,8 @@ from django.urls import reverse from django.shortcuts import render -from pontoon.base.models import Locale from pontoon.base.tests import ( + EntityFactory, ResourceFactory, TranslationFactory, TranslatedResourceFactory, @@ -16,44 +16,22 @@ @pytest.mark.django_db @patch("pontoon.localizations.views.render", wraps=render) -@patch.object(Locale, "parts_stats") -def test_latest_activity(mock_parts_stats, mock_render, client, project_a, locale_a): +def test_ajax_resources(mock_render, client, project_a, locale_a): """Ensure that the latest_activity field is added to parts.""" ProjectLocaleFactory.create(locale=locale_a, project=project_a) + resource = ResourceFactory.create(project=project_a, path="has/stats.po") resource2 = ResourceFactory.create(project=project_a, path="has/stats2.po") - translation = TranslationFactory.create(entity__resource=resource, locale=locale_a) - TranslatedResourceFactory.create( + entity = EntityFactory.create(resource=resource) + EntityFactory.create(resource=resource2) + translation = TranslationFactory.create(entity=entity, locale=locale_a) + + tr = TranslatedResourceFactory.create( resource=resource2, locale=locale_a, latest_translation=translation ) - - mock_parts_stats.return_value = [ - { - "title": "has/stats.po", - "resource__path": "has/stats.po", - "resource__total_strings": 1, - "approved_strings": 0, - "unreviewed_strings": 1, - "pretranslated_strings": 0, - "strings_with_warnings": 0, - "strings_with_errors": 0, - "resource__deadline": None, - "resource__priority": None, - }, - { - "title": "no/stats.po", - "resource__path": "no/stats.po", - "resource__total_strings": 1, - "approved_strings": 0, - "unreviewed_strings": 0, - "pretranslated_strings": 0, - "strings_with_warnings": 0, - "strings_with_errors": 0, - "resource__deadline": None, - "resource__priority": None, - }, - ] + tr.total_strings = 1 + tr.save() client.get( reverse( @@ -65,59 +43,23 @@ def test_latest_activity(mock_parts_stats, mock_render, client, project_a, local ctx = mock_render.call_args[0][2] - assert ctx["resources"] == [ - { - "latest_activity": translation.latest_activity, - "title": "has/stats.po", - "resource__path": "has/stats.po", - "resource__deadline": None, - "resource__priority": None, - "resource__total_strings": 1, - "approved_strings": 0, - "unreviewed_strings": 1, - "pretranslated_strings": 0, - "strings_with_errors": 0, - "strings_with_warnings": 0, - "chart": { - "pretranslated_strings": 0, - "total_strings": 1, - "approved_strings": 0, - "unreviewed_strings": 1, - "strings_with_errors": 0, - "strings_with_warnings": 0, - "approved_share": 0.0, - "unreviewed_share": 100.0, - "pretranslated_share": 0.0, - "warnings_share": 0.0, - "errors_share": 0.0, - "completion_percent": 0, - }, - }, - { - "latest_activity": None, - "title": "no/stats.po", - "resource__path": "no/stats.po", - "resource__deadline": None, - "resource__priority": None, - "resource__total_strings": 1, - "approved_strings": 0, - "unreviewed_strings": 0, - "pretranslated_strings": 0, - "strings_with_errors": 0, - "strings_with_warnings": 0, - "chart": { - "pretranslated_strings": 0, - "total_strings": 1, - "approved_strings": 0, - "unreviewed_strings": 0, - "strings_with_errors": 0, - "strings_with_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, - }, - }, - ] + assert len(ctx["resources"]) == 2 + + assert ctx["resources"][0].title == "has/stats2.po" + assert ctx["resources"][0].deadline is None + assert ctx["resources"][0].priority is None + assert ctx["resources"][0].latest_activity == translation.latest_activity + assert ctx["resources"][0].chart == { + "pretranslated_strings": 0, + "total_strings": 1, + "approved_strings": 0, + "unreviewed_strings": 0, + "strings_with_errors": 0, + "strings_with_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 4e92b25378..7cac191d88 100644 --- a/pontoon/localizations/views.py +++ b/pontoon/localizations/views.py @@ -1,4 +1,3 @@ -import math from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.db.models import Q @@ -42,7 +41,15 @@ def localization(request, code, slug): project=project, ) - resource_count = len(locale.parts_stats(project)) - 1 + resource_count = ( + TranslatedResource.objects.filter( + resource__project=project, + locale=locale, + resource__entities__obsolete=False, + ) + .distinct() + .count() + ) return render( request, @@ -73,65 +80,33 @@ def ajax_resources(request, code, slug): # Check if ProjectLocale exists get_object_or_404(ProjectLocale, locale=locale, project=project) - # Amend the parts dict with latest activity info. - translatedresources_qs = TranslatedResource.objects.filter( - resource__project=project, locale=locale - ).prefetch_related("resource", "latest_translation__user") + # Prefetch data needed for the latest activity column + translatedresources = ( + TranslatedResource.objects.filter( + resource__project=project, + locale=locale, + resource__entities__obsolete=False, + ) + .order_by("resource__path") + .prefetch_related( + "resource", "latest_translation__user", "latest_translation__approved_user" + ) + .distinct() + ) - if not len(translatedresources_qs): + if not len(translatedresources): raise Http404 - translatedresources = {s.resource.path: s for s in translatedresources_qs} - translatedresources = dict(list(translatedresources.items())) - parts = locale.parts_stats(project) - resource_priority_map = project.resource_priority_map() - for part in parts: - part["resource__priority"] = resource_priority_map.get(part["title"], None) - - translatedresource = translatedresources.get(part["title"], None) - if translatedresource and translatedresource.latest_translation: - part[ - "latest_activity" - ] = translatedresource.latest_translation.latest_activity - else: - part["latest_activity"] = None - - part["chart"] = { - "unreviewed_strings": part["unreviewed_strings"], - "pretranslated_strings": part["pretranslated_strings"], - "strings_with_errors": part["strings_with_errors"], - "strings_with_warnings": part["strings_with_warnings"], - "total_strings": part["resource__total_strings"], - "approved_strings": part["approved_strings"], - "approved_share": round( - part["approved_strings"] / part["resource__total_strings"] * 100 - ), - "unreviewed_share": round( - part["unreviewed_strings"] / part["resource__total_strings"] * 100 - ), - "pretranslated_share": round( - part["pretranslated_strings"] / part["resource__total_strings"] * 100 - ), - "errors_share": round( - part["strings_with_errors"] / part["resource__total_strings"] * 100 - ), - "warnings_share": round( - part["strings_with_warnings"] / part["resource__total_strings"] * 100 - ), - "completion_percent": int( - math.floor( - ( - part["approved_strings"] - + part["pretranslated_strings"] - + part["strings_with_warnings"] - ) - / part["resource__total_strings"] - * 100 - ) - ), - } + for tr in translatedresources: + tr.title = tr.resource.path + tr.deadline = tr.resource.deadline + tr.priority = resource_priority_map.get(tr.resource.path, None) + tr.latest_activity = ( + tr.latest_translation.latest_activity if tr.latest_translation else None + ) + tr.chart = TranslatedResource.get_chart_dict(tr) return render( request, @@ -139,9 +114,9 @@ def ajax_resources(request, code, slug): { "locale": locale, "project": project, - "resources": parts, - "deadline": any(part["resource__deadline"] for part in parts), - "priority": any(part["resource__priority"] for part in parts), + "resources": translatedresources, + "deadline": any(tr.resource.deadline for tr in translatedresources), + "priority": any(tr.priority for tr in translatedresources), }, )