Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 28 additions & 12 deletions pontoon/insights/chs.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,11 @@ def get_key_projects_enabled_by_locale(
return {pl_count["locale_id"]: pl_count["count"] for pl_count in pl_counts}


def get_contributor_metrics_by_locale(locales, end_date: datetime) -> dict[int, dict]:
def get_contributor_classification_by_locale(
locales, end_date: datetime
) -> dict[int, dict]:
"""
Per-locale active-contributor counts over the 12-month window ending at end_date.
Per-locale active-contributor lists over the 12-month window ending at end_date.
"""
start_date = end_date - relativedelta(months=13)

Expand Down Expand Up @@ -149,11 +151,11 @@ def get_contributor_metrics_by_locale(locales, end_date: datetime) -> dict[int,

locale_contributors = {
locale.pk: {
"active_managers": 0,
"active_translators": 0,
"active_contributors": 0,
"all_contributors": 0,
"new_signups": 0,
"active_managers": set(),
"active_translators": set(),
"active_contributors": set(),
"all_contributors": set(),
"new_signups": set(),
}
for locale in locales
}
Expand All @@ -173,23 +175,37 @@ def get_contributor_metrics_by_locale(locales, end_date: datetime) -> dict[int,

if user_id in managers[locale_id]:
if action_count + approved > MANAGER_STRING_THRESHOLD:
locale_contributors[locale_id]["active_managers"] += 1
locale_contributors[locale_id]["active_managers"].add(user_id)
elif user_id in translators[locale_id]:
if action_count + approved > TRANSLATOR_STRING_THRESHOLD:
locale_contributors[locale_id]["active_translators"] += 1
locale_contributors[locale_id]["active_translators"].add(user_id)
else:
if is_superuser:
continue
if approved >= ACTIVE_CONTRIBUTOR_STRING_THRESHOLD:
locale_contributors[locale_id]["active_contributors"] += 1
locale_contributors[locale_id]["active_contributors"].add(user_id)
if total >= ALL_CONTRIBUTOR_STRING_THRESHOLD:
locale_contributors[locale_id]["all_contributors"] += 1
locale_contributors[locale_id]["all_contributors"].add(user_id)
if approved >= NEW_SIGNUP_STRING_THRESHOLD and joined >= start_date:
locale_contributors[locale_id]["new_signups"] += 1
locale_contributors[locale_id]["new_signups"].add(user_id)

return locale_contributors


def get_contributor_metrics_by_locale(locales, end_date: datetime) -> dict[int, dict]:
"""
Per-locale active-contributor counts over the 12-month window ending at end_date.
"""
locale_contributors = get_contributor_classification_by_locale(locales, end_date)

locale_contributor_counts = {
loc_id: {metric: len(contributors) for metric, contributors in counts.items()}
for loc_id, counts in locale_contributors.items()
}

return locale_contributor_counts


def scaled_points(count, points) -> float:
"""Award full points for 2+ people, half for exactly 1, none otherwise."""
if count >= 2:
Expand Down
47 changes: 47 additions & 0 deletions pontoon/insights/static/css/insights.css
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,58 @@

.info {
align-self: center;
position: relative;

&.met {
color: var(--status-translated);
font-weight: bold;
}

.metric-tooltip {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
z-index: 20;
min-width: 140px;
margin-bottom: 10px;
padding: 10px;
background: var(--tooltip-background);
border-radius: 10px;
color: var(--tooltip-color);
font-weight: normal;
text-align: left;

&:after {
content: '';
position: absolute;
bottom: -20px;
left: 50%;
transform: translateX(-50%);
border: 10px solid;
border-color: var(--tooltip-background) transparent transparent
transparent;
clip-path: polygon(0 0, 100% 0, 100% 50%, 0 50%);
}

ul {
margin: 0;
padding: 0;
list-style: none;
}

li {
list-style: none;
}

a {
display: flex;
align-items: center;
gap: 6px;
color: var(--tooltip-color);
white-space: nowrap;
}
}
}

&.score-view {
Expand Down
55 changes: 55 additions & 0 deletions pontoon/insights/static/js/insights.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,61 @@ function renderCommunityHealthPanel() {
});
}

let contributorTooltipTimer = null;
let activeContributorInfo = null;

$('body')
.on('mouseenter', '.community-health-table .info[data-metric]', function () {
const info = $(this);
activeContributorInfo = this;

contributorTooltipTimer = setTimeout(function () {
$.ajax({
url: '/insights/ajax/locale-contributors/',
global: false,
data: {
locale: info.data('locale'),
metric: info.data('metric'),
},
success(response) {
if (activeContributorInfo !== info[0] || !response.users) {
return;
}

const list = $('<ul>');

if (response.users.length === 0) {
list.append($('<li class="empty">').text('No contributors'));
} else {
response.users.forEach(function (user) {
const link = $('<a>').attr(
'href',
'/contributors/' + user.username + '/',
);
link.append(
$('<img class="rounded">')
.attr('width', 24)
.attr('height', 24)
.attr('src', user.avatar),
);
link.append($('<span>').text(user.name));
list.append($('<li>').append(link));
});
}

info.append($('<div class="metric-tooltip">').append(list));
},
});
}, 500);
})
.on('mouseleave', '.community-health-table .info[data-metric]', function () {
clearTimeout(contributorTooltipTimer);
if (activeContributorInfo === this) {
activeContributorInfo = null;
}
$(this).find('.metric-tooltip').remove();
});

$('#edit-locales').on('click', function (e) {
e.preventDefault();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@

{% macro value_cell(base_value, base_delta=None, base_threshold=None,
score_value=None, score_delta=None, score_threshold=None,
percent=False, single=False) %}
percent=False, single=False, locale_id=None, metric=None) %}
<td class="cell" data-sort="{{ base_value }}"
{% if not single %} data-base-sort="{{ base_value }}" data-score-sort="{{ score_value }}"{% endif %}>
{% if single %}
Expand All @@ -61,13 +61,13 @@
{% else %}
<div class="info-container base-view">
{{ delta_span(base_delta, percent) }}
<span class="info {% if base_threshold and base_value >= base_threshold %}met{% endif %}">
<span class="info {% if base_threshold and base_value >= base_threshold %}met{% endif %}"{% if metric and base_value > 0 %} data-locale="{{ locale_id }}" data-metric="{{ metric }}"{% endif %}>
{{ base_value }}{{ '%' if percent }}
</span>
</div>
<div class="info-container score-view">
{{ delta_span(score_delta, false) }}
<span class="info {% if score_threshold and score_value >= score_threshold %}met{% endif %}">
<span class="info {% if score_threshold and score_value >= score_threshold %}met{% endif %}"{% if metric and base_value > 0 %} data-locale="{{ locale_id }}" data-metric="{{ metric }}"{% endif %}>
{{ score_value }}
</span>
</div>
Expand All @@ -76,6 +76,7 @@
{% endmacro %}

{% macro item(locale, main_link, curr_snapshot=None, base_deltas=None, score_deltas=None, columns=None, class='limited') %}
{% set contributor_columns = ['active_managers', 'active_translators', 'active_contributors', 'all_contributors', 'new_signups'] %}
<tr class="{{ class }}">
<td class="code">
<div>
Expand All @@ -98,7 +99,9 @@
score_value=curr_snapshot[field + '_score'],
score_delta=score_deltas[field + '_score'] if score_deltas else None,
score_threshold=column.score_threshold,
percent=column.percent) }}
percent=column.percent,
locale_id=locale.id,
metric=field if field in contributor_columns else none) }}
{% endif %}
{% endfor %}
{% else %}
Expand Down
12 changes: 11 additions & 1 deletion pontoon/insights/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from django.urls import include, path

from pontoon.insights.views import edit_locales, insights, render_panel
from pontoon.insights.views import (
edit_locales,
get_locale_contributors,
insights,
render_panel,
)


urlpatterns = [
Expand Down Expand Up @@ -29,6 +34,11 @@
render_panel,
name="pontoon.insights.render_panel",
),
path(
"locale-contributors/",
get_locale_contributors,
name="pontoon.insights.locale_contributors",
),
]
),
),
Expand Down
Loading
Loading