Add contribution graph to release report. (#1480)

This commit is contained in:
Brian Perrett
2024-11-21 12:15:10 -08:00
committed by GitHub
parent 15698ee478
commit 2503ee6a08
2 changed files with 150 additions and 0 deletions

View File

@@ -1,7 +1,10 @@
import io
import base64
from functools import cached_property
from itertools import groupby
from operator import attrgetter
from dataclasses import dataclass, field
from datetime import date, timedelta
import psycopg2
from wordcloud import WordCloud, STOPWORDS
@@ -501,6 +504,110 @@ class CreateReportForm(CreateReportFullForm):
key=lambda x: library_order.index(x.library_id),
)
def _get_git_graph_data(self, prior_version: Version | None, version: Version):
"""Fetch commit count data for a release and return an instance of Graph.
Returns data in a format to easily create a github style green box commit graph.
"""
if prior_version is None:
return None
if prior_version.release_date is None or version.release_date is None:
return None
@dataclass
class Day:
date: date
count: int
color: str = ""
@dataclass
class Week:
days: list[Day] = field(default_factory=list)
@cached_property
def max(self):
"""The max number of commits this week."""
return max(x.count for x in self.days)
@dataclass
class Graph:
weeks: list[Week] = field(default_factory=list)
colors: list[str] = field(
default_factory=lambda: [
"#E8F5E9",
"#C8E6C9",
"#A5D6A7",
"#81C784",
"#66BB6A",
"#4CAF50",
"#43A047",
"#388E3C",
"#2E7D32",
"#1B5E20",
],
)
@cached_property
def max(self):
"""The max number of commits in all weeks."""
return max(x.max for x in self.weeks)
def append_day(self, day: Day):
"""Append a day into the last week of self.weeks.
- Automatically create a new week if there are already 7 days in the
last week.
"""
if len(self.weeks) == 0 or len(self.weeks[-1].days) == 7:
self.weeks.append(Week())
self.weeks[-1].days.append(day)
def apply_colors(self):
"""Iterate through each day and apply a color.
- The color is selected based on the number of commits made on
that day, relative to the highest number of commits in all days in
Graph.weeks.days.
"""
high = self.max
for week in self.weeks:
for day in week.days:
decimal = day.count / high
if decimal == 1:
day.color = self.colors[-1]
else:
idx = int(decimal * len(self.colors))
day.color = self.colors[idx]
count_query = (
Commit.objects.filter(library_version__version=version)
.values("committed_at__date")
.annotate(count=Count("id"))
)
counts_by_date = {x["committed_at__date"]: x["count"] for x in count_query}
graph = Graph()
# The start date is the release date of the previous version
# The end date is one day before the release date of the current version
start: date = prior_version.release_date
end: date = version.release_date - timedelta(days=1)
# if the release started on a Thursday, we want to add Sun -> Wed to the data
# with empty counts, even if they aren't part of the release.
for i in range(start.weekday(), 0, -1):
day = Day(date=start - timedelta(days=i), count=0)
graph.append_day(day)
current_date = start
while current_date <= end:
day = Day(date=current_date, count=counts_by_date.get(current_date, 0))
graph.append_day(day)
current_date = current_date + timedelta(days=1)
graph.apply_colors()
return graph
def get_stats(self):
version = self.cleaned_data["version"]
@@ -607,6 +714,7 @@ class CreateReportForm(CreateReportFullForm):
"lines_removed": lines_removed,
"wordcloud_base64": self._generate_hyperkitty_word_cloud(version),
"version": version,
"prior_version": prior_version,
"opened_issues_count": Issue.objects.filter(
library__in=self.library_queryset
)
@@ -634,4 +742,5 @@ class CreateReportForm(CreateReportFullForm):
"added_library_count": added_library_count,
"removed_library_count": removed_library_count,
"downloads": downloads,
"contribution_box_graph": self._get_git_graph_data(prior_version, version),
}

View File

@@ -55,6 +55,47 @@
</table>
{% endif %}
</div>
<div class="flex flex-col">
{% if contribution_box_graph %}
<div class="flex flex-col gap-y-4">
<div class="flex gap-x-1">
{% for week in contribution_box_graph.weeks %}
<div class="flex flex-col gap-y-1">
<div class="h-4 relative">
{% ifchanged week.days.0.date.month %}
{% if forloop.counter0 != 0 %}
<div class="absolute top-0 left-[-5px] text-sm font-bold">
{{ week.days.0.date|date:"M" }}
</div>
{% endif %}
{% endifchanged %}
</div>
{% for day in week.days %}
<div
class="w-4 h-4 rounded-sm"
{% if day.date >= prior_version.release_date %}
style="background-color: {{ day.color }};"
{% endif %}
title="{{ day.count }} commit{{ day.count|pluralize }} on {{ day.date }}"
>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
<div class="flex gap-x-1 text-sm w-full mx-auto">
<div class="mr-1">0 commits</div>
{% for color in contribution_box_graph.colors %}
<div class="h-4 w-4 rounded-sm" style="background-color: {{ color }}">
</div>
{% endfor %}
<div class="ml-1">{{ contribution_box_graph.max }} commits</div>
</div>
</div>
{% endif %}
</div>
</div>
<div class="pdf-page grid grid-cols-2 gap-x-4 items-center justify-items-center {{ bg_color }}">
<div class="flex flex-col">