Repository: mozilla/pontoon

Issue:
Add ability to show project stats for a subset of teams
Devs and PMs would like to see project stats for a subset of teams only, especially for projects we ship in several tiers of markets. This issue tracks the implementation of:

1. Ability to define subsets of teams in the project admin.
2. Ability to show project stats for each subset of teams.

In a followup, we'd also like to have the: 

1. Ability to switch between the subset(s) of teams and all teams in the project dashboard.
2. Reflect selected subset of locales in the Contributor and Insights tabs.

Mockup:
<img width="1059" alt="Screenshot 2021-09-20 at 15 55 18" src="https://user-images.githubusercontent.com/626716/134014557-2eed2ea3-a602-47b0-9018-e9d90a9b907a.png">


Discussion:
> Ability to define subsets of teams in the project admin.

What if these could be passed as part of the URL? Is seem a lot more flexible, and might not require much more work in terms of implementation. I don't think you could expose that easily via UI, but maybe it's OK for such a feature to be half hidden?
That would actually be simpler implement, we could ship it pretty fast. @Delphine and I also talked about making the feature less visible.

@Delphine Would an URL like https://pontoon.mozilla.org/projects/firefox-for-ios/?teams=ar,zh-CN,fr,de,it,pl,sl (which would take you to the page shown in the mockup above) be sufficient, at least for the first iteration?

Resolving patch:
diff --git a/pontoon/base/models.py b/pontoon/base/models.py
--- a/pontoon/base/models.py
+++ b/pontoon/base/models.py
@@ -1617,6 +1617,16 @@ def __str__(self):
 
 
 class ProjectLocaleQuerySet(models.QuerySet):
+    def aggregated_stats(self):
+        return self.aggregate(
+            total_strings=Sum("total_strings"),
+            approved_strings=Sum("approved_strings"),
+            fuzzy_strings=Sum("fuzzy_strings"),
+            strings_with_errors=Sum("strings_with_errors"),
+            strings_with_warnings=Sum("strings_with_warnings"),
+            unreviewed_strings=Sum("unreviewed_strings"),
+        )
+
     def visible_for(self, user):
         """
         Filter project locales by the visibility of their projects.
diff --git a/pontoon/projects/views.py b/pontoon/projects/views.py
--- a/pontoon/projects/views.py
+++ b/pontoon/projects/views.py
@@ -46,10 +46,23 @@ def project(request, slug):
     project = get_object_or_404(
         Project.objects.visible_for(request.user).available(), slug=slug
     )
+
+    project_locales = project.project_locale
+    chart = project
+
+    # Only include filtered teams if provided
+    teams = request.GET.get("teams", "").split(",")
+    filtered_locales = Locale.objects.filter(code__in=teams)
+    if filtered_locales.exists():
+        project_locales = project_locales.filter(locale__in=filtered_locales)
+        chart = project_locales.aggregated_stats()
+
     return render(
         request,
         "projects/project.html",
         {
+            "chart": chart,
+            "count": project_locales.count(),
             "project": project,
             "tags": (
                 len(TagsTool(projects=[project], priority=True))
@@ -67,9 +80,15 @@ def ajax_teams(request, slug):
         Project.objects.visible_for(request.user).available(), slug=slug
     )
 
-    locales = (
-        Locale.objects.available().prefetch_project_locale(project).order_by("name")
-    )
+    locales = Locale.objects.available()
+
+    # Only include filtered teams if provided
+    teams = request.GET.get("teams", "").split(",")
+    filtered_locales = Locale.objects.filter(code__in=teams)
+    if filtered_locales.exists():
+        locales = locales.filter(pk__in=filtered_locales)
+
+    locales = locales.prefetch_project_locale(project).order_by("name")
 
     return render(
         request,


Test patch:
diff --git a/pontoon/projects/tests/test_views.py b/pontoon/projects/tests/test_views.py
--- a/pontoon/projects/tests/test_views.py
+++ b/pontoon/projects/tests/test_views.py
@@ -39,6 +39,19 @@ def test_project_view(client, project_a, resource_a):
         assert mock_render.call_args[0][2]["project"] == project_a
 
 
+@pytest.mark.django_db
+def test_project_view_filtered_teams(
+    client, locale_a, project_a, project_locale_a, resource_a
+):
+    """
+    Checks if project page is returned properly with filtered teams.
+    """
+    with patch("pontoon.projects.views.render", wraps=render) as mock_render:
+        client.get(f"/projects/{project_a.slug}/?teams={locale_a.code}")
+        assert mock_render.call_args[0][2]["project"] == project_a
+        assert mock_render.call_args[0][2]["count"] == 1
+
+
 @pytest.mark.django_db
 def test_project_top_contributors(client, project_a, project_b):
     """