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),
},
)