Skip to content

Commit

Permalink
Add slack activity to release report. (#1490)
Browse files Browse the repository at this point in the history
  • Loading branch information
brianjp93 authored Nov 27, 2024
1 parent d5b37c8 commit 350119e
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 0 deletions.
63 changes: 63 additions & 0 deletions libraries/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from django.conf import settings

from core.models import RenderedContent, SiteSettings
from libraries.utils import batched
from slack.models import Channel, SlackActivityBucket, SlackUser
from versions.models import Version
from .models import Commit, CommitAuthor, Issue, Library, LibraryVersion
from libraries.constants import SUB_LIBRARIES
Expand Down Expand Up @@ -608,6 +610,64 @@ def apply_colors(self):
graph.apply_colors()
return graph

def _get_slack_stats(self, prior_version, version):
"""Returns all slack related stats."""
stats = []
for channel in Channel.objects.filter(name__istartswith="boost"):
channel_stat = self._get_slack_stats_for_channels(
prior_version, version, channels=[channel]
)
channel_stat["channel"] = channel
stats.append(channel_stat)
stats.sort(key=lambda x: -x["total"])
return stats

def _get_slack_stats_for_channels(
self, prior_version, version, channels: list[Channel] | None = None
):
"""Get slack stats for specific channels, or all channels."""
start = prior_version.release_date
end = version.release_date - timedelta(days=1)
# count of all messages in the date range
q = Q(day__range=[start, end])
if channels:
q &= Q(channel__in=channels)
total = SlackActivityBucket.objects.filter(q).aggregate(total=Sum("count"))[
"total"
]
# message counts per user in the date range
q = Q(slackactivitybucket__day__range=[start, end])
if channels:
q &= Q(slackactivitybucket__channel__in=channels)
per_user = (
SlackUser.objects.annotate(
total=Sum(
"slackactivitybucket__count",
filter=q,
)
)
.filter(total__gt=0)
.order_by("-total")
)
q = Q()
if channels:
q &= Q(channel__in=channels)
distinct_users = (
SlackActivityBucket.objects.filter(q)
.order_by("user_id")
.distinct("user_id")
)
new_user_count = (
distinct_users.filter(day__lte=end).count()
- distinct_users.filter(day__lt=start).count()
)
return {
"users": per_user[:10],
"user_count": per_user.count(),
"total": total,
"new_user_count": new_user_count,
}

def get_stats(self):
version = self.cleaned_data["version"]

Expand Down Expand Up @@ -709,6 +769,8 @@ def get_stats(self):
version=version,
library__in=self.library_queryset,
).aggregate(lines=Sum("deletions"))["lines"]
# we want 2 channels per pdf page, use batched to get groups of 2
slack_stats = batched(self._get_slack_stats(prior_version, version), 2)
return {
"lines_added": lines_added,
"lines_removed": lines_removed,
Expand Down Expand Up @@ -743,4 +805,5 @@ def get_stats(self):
"removed_library_count": removed_library_count,
"downloads": downloads,
"contribution_box_graph": self._get_git_graph_data(prior_version, version),
"slack": slack_stats,
}
13 changes: 13 additions & 0 deletions libraries/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from itertools import islice
import random
import string
import re
Expand Down Expand Up @@ -190,3 +191,15 @@ def find_documentation_url(library_version):
url = library_doc_latest_transform(url)

return url


def batched(iterable, n, *, strict=False):
# batched('ABCDEFG', 3) → ABC DEF G
# In python 3.12, this function can be deleted in favor of itertools.batched
if n < 1:
raise ValueError("n must be at least one")
iterator = iter(iterable)
while batch := tuple(islice(iterator, n)):
if strict and len(batch) != n:
raise ValueError("batched(): incomplete batch")
yield batch
44 changes: 44 additions & 0 deletions templates/admin/release_report_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,50 @@ <h1 class="mx-auto">Mailing List Word Cloud</h1>
</div>
</div>
{% endif %}
{% for slack_group in slack %}
<div class="pdf-page flex items-center justify-items-center {{ bg_color }}">
<div class="flex flex-col mx-auto">
<h1 class="mx-auto">Slack Activity</h1>
<div class="flex gap-x-4">
{% for slack_item in slack_group %}
<div class="flex flex-col mx-auto gap-y-2">
<div class="font-bold">
Top Contributors in #{{slack_item.channel.name}}
</div>
<div class="flex gap-x-2 mx-auto">
<div>
<div class="m-auto grid grid-cols-1 gap-2">
{% for item in slack_item.users %}
<div class="flex flex-row gap-y-2 w-40 items-center ml-6">
{% base_avatar image_url=item.image_48 name=item.real_name href=None %}
<div class="w-full flex flex-col ml-2">
<div class="text-[0.8rem] text-bold overflow-ellipsis overflow-hidden whitespace-nowrap w-full">
{{ item.real_name }}
</div>
<div class="text-[0.7rem]">{{ item.total }}</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<div>
<div class="mx-auto">
{{ slack_item.total|intcomma }}
slack message{{ slack_item.total|pluralize }} in #{{ slack_item.channel.name }}
</div>
<div class="mx-auto">
{{ slack_item.user_count }}
{{ slack_item.user_count|pluralize:"person,people" }}
conversing in this release. ({{ slack_item.new_user_count }} New)
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
<div class="pdf-page flex items-center justify-items-center {{ bg_color }}">
<div class="flex flex-col h-full mx-auto">
<h1 class="mx-auto">Library Index</h1>
Expand Down

0 comments on commit 350119e

Please sign in to comment.