Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
7592cd6
add chs cron job (not fully functional)
functionzz Jun 3, 2026
474b32d
Update pontoon/insights/chs.py
functionzz Jun 4, 2026
39ed6cc
Update pontoon/insights/chs.py
functionzz Jun 8, 2026
db3fd41
Update pontoon/insights/chs.py
functionzz Jun 8, 2026
a7f24ff
add env variables over hardcoded constants
functionzz Jun 8, 2026
257014a
change LocaleChsSnapshot to LocaleHealthSnapshot, completion formula …
functionzz Jun 10, 2026
a064a00
dashboard frontend base
functionzz Jun 11, 2026
7df3b42
add configuration form + button + model changes, add staff authentica…
functionzz Jun 12, 2026
9da467a
add score/default view, change fields to Decimal, reform css, improv…
functionzz Jun 12, 2026
8dd04bd
missing dashboard.html addition
functionzz Jun 12, 2026
93d6417
add no locale check, order locales by code
functionzz Jun 15, 2026
56f737f
full feature parity with scripts
functionzz Jun 15, 2026
56a5929
add community health activity charts to Locale and Global insights
functionzz Jun 22, 2026
1ec9668
small snapshot logic, docs + DX changes
functionzz Jun 22, 2026
db983ac
fix global insights tests
functionzz Jun 23, 2026
7014e00
fix typo
functionzz Jun 23, 2026
8ba21ff
Delete config.js
functionzz Jun 23, 2026
3370eb7
fix small nits, add locales param to build_chs_snapshots
functionzz Jun 23, 2026
9869c0a
add snapshot + supporting function tests
functionzz Jun 23, 2026
c4b12d7
rename columns, rename graphs, remove CHS Y-axis
functionzz Jun 23, 2026
49b1681
move /dashboard contents into /insights + renaming work
functionzz Jun 24, 2026
c4ce014
renaming dashboards
functionzz Jun 24, 2026
836bd41
doc changes
functionzz Jun 24, 2026
e2f4802
Create 0118_merge_20260624_0350.py
functionzz Jun 24, 2026
e0eade8
run make format
functionzz Jun 24, 2026
3be5b90
revert temp stopgap
functionzz Jun 24, 2026
6b12b9a
streamline LocaleHealthSnapshot migrations
functionzz Jun 24, 2026
898c2e7
run make format
functionzz Jun 24, 2026
845b226
merge migration
functionzz Jun 24, 2026
bf7a7a3
run make format
functionzz Jun 24, 2026
7f6dc85
rename collect_chs_snapshot file, center Locale, show All in graphs
functionzz Jun 25, 2026
6c7c17a
push All to top of global charts
functionzz Jun 25, 2026
4f4e716
Update collect_chs_snapshots.py
functionzz Jun 25, 2026
c982187
remove repetitive code
functionzz Jun 25, 2026
81862bc
cast envs to int
functionzz Jun 25, 2026
79cc24a
streamline migrations
functionzz Jun 26, 2026
0ca436b
update docs
functionzz Jun 26, 2026
7b2cabc
add noon anchor to chart data + tests
functionzz Jun 26, 2026
651a976
remove min-height
functionzz Jun 26, 2026
c062ef0
apply merged dashboard + chs chart, add tooltips, add "All" field for…
functionzz Jun 29, 2026
176345a
change title
functionzz Jun 30, 2026
97c44c2
add subheader
functionzz Jun 30, 2026
0f8fdf9
run make format
functionzz Jun 30, 2026
aa8e9b6
Update documentation/docs/dev/deployment.md
functionzz Jun 30, 2026
316e438
Update documentation/docs/dev/deployment.md
functionzz Jun 30, 2026
c37f637
Update pontoon/base/forms.py
functionzz Jun 30, 2026
9315c1f
Update pontoon/insights/management/commands/collect_chs_snapshots.py
functionzz Jun 30, 2026
489f10a
Update pontoon/insights/templates/insights/widgets/locale_list.html
functionzz Jun 30, 2026
869bc18
Update pontoon/insights/templates/insights/widgets/locale_list.html
functionzz Jun 30, 2026
2df3611
rename dashboard_locales to community_health_locales, rename form
functionzz Jun 30, 2026
7103cad
rename #view-toggle to #show-scores
functionzz Jun 30, 2026
5df9c40
rename team-community-health-chart to community-health-chart
functionzz Jun 30, 2026
384919a
rename locale_list.html to community_health_table.html + other relate…
functionzz Jun 30, 2026
db8e884
move tooltip style
functionzz Jun 30, 2026
9f89c11
remove unused css file from pipeline
functionzz Jun 30, 2026
dfb2fc8
move CommunityHealthLocaleForm
functionzz Jun 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions documentation/docs/dev/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,19 @@ the day, every day.
./manage.py collect_insights
```

### Collect Community Health Score Snapshots

Captures per-locale Community Health Score (CHS) metrics—including completion,
enabled key projects, active managers, translators, contributors, and new signups—
and stores them in the `LocaleHealthSnapshot` model.
These metrics are used by the Insights dashboard and related pages
to provide month-over-month CHS comparisons and monthly trend charts.
The job is designed to run once a month, on the first day of each month.

``` bash
./manage.py collect_chs_snapshots
```

### Warm up cache

We cache data for some of the views (e.g. Contributors) for a day. Some
Expand Down
Comment thread
functionzz marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 5.2.14 on 2026-06-26 16:58

import django.contrib.postgres.fields

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("base", "0118_fix_terminology_entity_value"),
]

operations = [
migrations.AddField(
model_name="userprofile",
name="community_health_locales",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.PositiveIntegerField(),
blank=True,
default=list,
size=None,
),
),
]
6 changes: 6 additions & 0 deletions pontoon/base/models/user_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ class EmailFrequencies(models.TextChoices):
search_match_whole_word = models.BooleanField(default=False)
search_rejected_translations = models.BooleanField(default=False)

community_health_locales = ArrayField(
models.PositiveIntegerField(),
default=list,
blank=True,
)

# Used to redirect a user to a custom team page.
custom_homepage = models.CharField(max_length=20, blank=True, null=True)

Expand Down
6 changes: 5 additions & 1 deletion pontoon/base/static/js/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,11 @@ var Pontoon = (function (my) {
}

function getSort(el) {
return parseInt($(el).find('[data-sort]').data('sort'), 10) || 0;
const cell = $(el).find('td').eq(index);
const holder = cell.is('[data-sort]')
? cell
: cell.find('[data-sort]').first();
return parseFloat(holder.attr('data-sort')) || 0;
}

function getString(el) {
Expand Down
15 changes: 12 additions & 3 deletions pontoon/base/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,20 @@ def aware_datetime(*args, **kwargs):
return make_aware(datetime(*args, **kwargs))


def convert_to_unix_time(my_datetime):
def convert_to_unix_time(my_datetime, anchor_noon=False):
"""
Convert datetime object to UNIX time
Convert datetime object to UNIX time (milliseconds).

anchor_noon=True is needed for datetimes that are plotted on charts
rendered in user's timezone. That's because times that are set on
the 1st of each month leak into the previous month for viewers
behind UTC. Anchoring at 12:00 UTC keeps data point inside the
correct calendar month for every timezone.
"""
return int(time.mktime(my_datetime.timetuple()) * 1000)
seconds = time.mktime(my_datetime.timetuple())
if anchor_noon:
seconds += 12 * 60 * 60
return int(seconds * 1000)


def sanitize_xml_input_string(string):
Expand Down
31 changes: 31 additions & 0 deletions pontoon/insights/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.contrib import admin

from pontoon.insights.models import (
LocaleHealthSnapshot,
LocaleInsightsSnapshot,
ProjectLocaleInsightsSnapshot,
)
Expand Down Expand Up @@ -39,5 +40,35 @@ class ProjectLocaleInsightsSnapshotAdmin(admin.ModelAdmin):
readonly_fields = ("project_locale",)


class LocaleHealthSnapshotAdmin(admin.ModelAdmin):
search_fields = [
"pk",
"locale__code",
"locale__name",
]
list_display = (
"pk",
"locale",
"created_at",
"completion",
"key_projects_enabled",
"active_managers",
"active_translators",
"active_contributors",
"all_contributors",
"new_signups",
"completion_score",
"key_projects_enabled_score",
"active_managers_score",
"active_translators_score",
"active_contributors_score",
"all_contributors_score",
"new_signups_score",
"chs",
)
list_filter = ("created_at",)


admin.site.register(LocaleInsightsSnapshot, LocaleInsightsSnapshotAdmin)
admin.site.register(ProjectLocaleInsightsSnapshot, ProjectLocaleInsightsSnapshotAdmin)
admin.site.register(LocaleHealthSnapshot, LocaleHealthSnapshotAdmin)
Loading
Loading