From d91611b829f0a6adbd95ea66fb112250f6c83eb2 Mon Sep 17 00:00:00 2001 From: Brian Perrett Date: Fri, 4 Oct 2024 16:01:44 -0700 Subject: [PATCH] Add library report over all releases. (#1310) - fixes #1288 --- libraries/admin.py | 61 ++++++-- libraries/forms.py | 129 ++++++++++++---- templates/admin/library_change_list.html | 3 +- templates/admin/library_report_base.html | 54 +++++++ templates/admin/library_report_form.html | 4 +- ...l.html => library_report_full_detail.html} | 85 ++--------- templates/admin/release_report_detail.html | 139 ++++++++++++++++++ 7 files changed, 358 insertions(+), 117 deletions(-) create mode 100644 templates/admin/library_report_base.html rename templates/admin/{library_report_detail.html => library_report_full_detail.html} (53%) create mode 100644 templates/admin/release_report_detail.html diff --git a/libraries/admin.py b/libraries/admin.py index 979ef329..85fdb62c 100644 --- a/libraries/admin.py +++ b/libraries/admin.py @@ -8,7 +8,7 @@ from django.urls import path, reverse from django.utils.safestring import mark_safe from django.shortcuts import redirect -from libraries.forms import CreateReportForm +from libraries.forms import CreateReportForm, CreateReportFullForm from versions.tasks import import_all_library_versions from .models import ( Category, @@ -139,40 +139,75 @@ class LibraryAdmin(admin.ModelAdmin): name="library_stat_detail", ), path( - "report-form/", - self.admin_site.admin_view(self.report_form_view), - name="library_report_form", + "release-report-form/", + self.admin_site.admin_view(self.release_report_form), + name="release_report_form", ), path( - "report/", - self.admin_site.admin_view(self.report_view), - name="library_report", + "release-report/", + self.admin_site.admin_view(self.release_report_view), + name="release_report", + ), + path( + "report-full-form/", + self.admin_site.admin_view(self.report_form_full_view), + name="library_report_full_form", + ), + path( + "report-full/", + self.admin_site.admin_view(self.report_full_view), + name="library_report_full", ), ] return my_urls + urls - def report_form_view(self, request): + def release_report_form(self, request): form = CreateReportForm() context = {} - if request.GET.get("version", None): + if request.GET.get("submit", None): form = CreateReportForm(request.GET) if form.is_valid(): context.update(form.get_stats()) return redirect( - reverse("admin:library_report") + f"?{request.GET.urlencode()}" + reverse("admin:release_report") + f"?{request.GET.urlencode()}" ) if not context: context["form"] = form return TemplateResponse(request, "admin/library_report_form.html", context) - def report_view(self, request): + def release_report_view(self, request): form = CreateReportForm(request.GET) context = {"form": form} if form.is_valid(): context.update(form.get_stats()) else: - return redirect("admin:library_report_form") - return TemplateResponse(request, "admin/library_report_detail.html", context) + return redirect("admin:release_report_form") + return TemplateResponse(request, "admin/release_report_detail.html", context) + + def report_form_full_view(self, request): + form = CreateReportFullForm() + context = {} + if request.GET.get("submit", None): + form = CreateReportFullForm(request.GET) + if form.is_valid(): + context.update(form.get_stats()) + return redirect( + reverse("admin:library_report_full") + f"?{request.GET.urlencode()}" + ) + if not context: + context["form"] = form + return TemplateResponse(request, "admin/library_report_form.html", context) + + def report_full_view(self, request): + form = CreateReportFullForm(request.GET) + context = {"form": form} + if form.is_valid(): + context.update(form.get_stats()) + else: + return redirect("admin:library_report_full_form") + return TemplateResponse( + request, "admin/library_report_full_detail.html", context + ) def view_stats(self, instance): url = reverse("admin:library_stat_detail", kwargs={"pk": instance.pk}) diff --git a/libraries/forms.py b/libraries/forms.py index 88c462fb..66ee756d 100644 --- a/libraries/forms.py +++ b/libraries/forms.py @@ -22,19 +22,14 @@ class VersionSelectionForm(Form): ) -class CreateReportForm(Form): +class CreateReportFullForm(Form): + """Form for creating a report over all releases.""" + library_queryset = Library.objects.all().order_by("name") - version = ModelChoiceField( - queryset=Version.objects.active() - .exclude(name__in=["develop", "master", "head"]) - .order_by("-name") - ) library_1 = ModelChoiceField( queryset=library_queryset, required=False, - help_text=( - "If none are selected, the top 5 for this release will be auto-selected." - ), + help_text="If none are selected, the top 5 will be auto-selected.", ) library_2 = ModelChoiceField( queryset=library_queryset, @@ -65,28 +60,14 @@ class CreateReportForm(Form): required=False, ) - def _get_top_contributors_for_version(self): + def _get_top_libraries(self): return ( - CommitAuthor.objects.filter( - commit__library_version__version=self.cleaned_data["version"] - ) - .annotate(commit_count=Count("commit")) - .values("name", "avatar_url", "commit_count") - .order_by("-commit_count")[:10] - ) - - def _get_top_libraries_for_version(self): - return ( - Library.objects.filter( - library_version=LibraryVersion.objects.filter( - library=OuterRef("id"), version=self.cleaned_data["version"] - )[:1], - ) + Library.objects.all() .annotate(commit_count=Count("library_version__commit")) .order_by("-commit_count")[:5] ) - def _get_library_order(self, top_libraries_release): + def _get_library_order(self, top_libraries): library_order = [ x.id for x in [ @@ -102,7 +83,7 @@ class CreateReportForm(Form): if x is not None ] if not library_order: - library_order = [x.id for x in top_libraries_release] + library_order = [x.id for x in top_libraries] return library_order def _get_library_full_counts(self, libraries, library_order): @@ -115,6 +96,99 @@ class CreateReportForm(Form): key=lambda x: library_order.index(x["id"]), ) + def _get_top_contributors_overall(self): + return ( + CommitAuthor.objects.all() + .annotate(commit_count=Count("commit")) + .values("name", "avatar_url", "commit_count", "github_profile_url") + .order_by("-commit_count")[:10] + ) + + def _get_top_contributors_for_library(self, library_order): + top_contributors_library = [] + for library_id in library_order: + top_contributors_library.append( + CommitAuthor.objects.filter( + commit__library_version__library_id=library_id + ) + .annotate(commit_count=Count("commit")) + .values( + "name", + "avatar_url", + "github_profile_url", + "commit_count", + "commit__library_version__library_id", + ) + .order_by("-commit_count")[:10] + ) + return top_contributors_library + + def get_stats(self): + commit_count = Commit.objects.count() + + top_libraries = self._get_top_libraries() + library_order = self._get_library_order(top_libraries) + libraries = Library.objects.filter(id__in=library_order) + library_data = [ + { + "library": x[0], + "full_count": x[1], + "top_contributors": x[2], + } + for x in zip( + sorted(list(libraries), key=lambda x: library_order.index(x.id)), + self._get_library_full_counts(libraries, library_order), + self._get_top_contributors_for_library(library_order), + ) + ] + top_contributors = self._get_top_contributors_overall() + return { + "commit_count": commit_count, + "top_contributors": top_contributors, + "library_data": library_data, + "top_libraries": top_libraries, + "library_count": Library.objects.all().count(), + } + + +class CreateReportForm(CreateReportFullForm): + """Form for creating a report for a specific release.""" + + version = ModelChoiceField( + queryset=Version.objects.active() + .exclude(name__in=["develop", "master", "head"]) + .order_by("-name") + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields[ + "library_1" + ].help_text = ( + "If none are selected, the top 5 for this release will be auto-selected." + ) + + def _get_top_contributors_for_version(self): + return ( + CommitAuthor.objects.filter( + commit__library_version__version=self.cleaned_data["version"] + ) + .annotate(commit_count=Count("commit")) + .values("name", "avatar_url", "commit_count", "github_profile_url") + .order_by("-commit_count")[:10] + ) + + def _get_top_libraries_for_version(self): + return ( + Library.objects.filter( + library_version=LibraryVersion.objects.filter( + library=OuterRef("id"), version=self.cleaned_data["version"] + )[:1], + ) + .annotate(commit_count=Count("library_version__commit")) + .order_by("-commit_count")[:5] + ) + def _get_library_version_counts(self, libraries, library_order): return sorted( list( @@ -173,6 +247,7 @@ class CreateReportForm(Form): .values( "name", "avatar_url", + "github_profile_url", "commit_count", "commit__library_version__library_id", ) diff --git a/templates/admin/library_change_list.html b/templates/admin/library_change_list.html index c873f311..984d6376 100644 --- a/templates/admin/library_change_list.html +++ b/templates/admin/library_change_list.html @@ -6,7 +6,8 @@ {% block object-tools-items %} {{ block.super }}
  • {% trans "Update Library Data" %}
  • -
  • {% trans "Get Release Report" %}
  • +
  • {% trans "Get Release Report" %}
  • +
  • {% trans "Get Library Report" %}
  • {% endblock %} {% endblock %} diff --git a/templates/admin/library_report_base.html b/templates/admin/library_report_base.html new file mode 100644 index 00000000..a95c8c2c --- /dev/null +++ b/templates/admin/library_report_base.html @@ -0,0 +1,54 @@ +{% load static humanize avatar_tags %} + + + + + {% block title %}Boost{% endblock %} + + + + + + + + + + + + + + + + + + {% block extra_head %} + + {% endblock %} + {% block css %} + + {% endblock css %} + + + {% block content %} + {% endblock content %} + + diff --git a/templates/admin/library_report_form.html b/templates/admin/library_report_form.html index 08b50c4f..e72fe56f 100644 --- a/templates/admin/library_report_form.html +++ b/templates/admin/library_report_form.html @@ -3,11 +3,11 @@ {% block content %} {{ block.super }}
    -

    Generate Report for Release

    +

    Generate Report

    {{ form.as_p }} - +
    diff --git a/templates/admin/library_report_detail.html b/templates/admin/library_report_full_detail.html similarity index 53% rename from templates/admin/library_report_detail.html rename to templates/admin/library_report_full_detail.html index d5c7e503..a47c484e 100644 --- a/templates/admin/library_report_detail.html +++ b/templates/admin/library_report_full_detail.html @@ -1,70 +1,17 @@ -{% load static humanize avatar_tags %} - - - - - {% block title %}Boost{% endblock %} - - - - - - - - - - - - - - - - - - {% block extra_head %} - - {% endblock %} - {% block css %} - {% endblock css %} - - +{% extends "admin/library_report_base.html" %} +{% load humanize avatar_tags %} +{% block content %} {% with bg_color='bg-gradient-to-tr from-[#7ac3e6]/50 to-[#d9b05e]/50' %}
    -
    -
    -

    Boost

    -
    {{ commit_count|intcomma }} commits up through {{ version.display_name }}
    -
    -
    There were {{ version_commit_count|intcomma }} commits in {{ version.display_name }}
    -
    -

    Boost {{ version.display_name }}

    -
    {{ version_commit_count|intcomma }} Commits Across {{ library_count }} Repositories
    +

    Boost

    +
    {{ commit_count|intcomma }} in all releases across all {{ library_count }} libraries
    - {% for author in top_contributors_release_overall %} + {% for author in top_contributors %}
    {% avatar commitauthor=author %}
    @@ -91,20 +38,11 @@
    {{ item.library.description }}
    -

    There were {{ item.version_count.commit_count }} commits in release {{ version.display_name }}

    - {% if item.new_contributors_count.count > 1 %} -
    - There were {{ item.new_contributors_count.count }} new contributors this release! -
    - {% elif item.new_contributors_count.count == 1 %} -
    - There was {{ item.new_contributors_count.count }} new contributor this release! -
    - {% endif %} +

    There are {{ item.full_count.commit_count|intcomma }} commits across all releases

    Top Contributors
    - {% for author in item.top_contributors_release %} + {% for author in item.top_contributors %}
    {% avatar commitauthor=author %}
    @@ -126,7 +64,7 @@ var options = { series: [{ name: 'Commits', - data: [{% for library in top_libraries_for_version %}{{library.commit_count}}, {% endfor %}] + data: [{% for library in top_libraries %}{{library.commit_count}}, {% endfor %}] }], chart: { height: 300, @@ -154,7 +92,7 @@ } }, xaxis: { - categories: [{% for library in top_libraries_for_version %} "{{ library.name }}", {% endfor %}], + categories: [{% for library in top_libraries %} "{{ library.name }}", {% endfor %}], position: 'bottom', axisBorder: { show: false @@ -182,6 +120,5 @@ const chart = new ApexCharts(document.querySelector("#top-committed-libraries-chart"), options); chart.render(); - {% endwith %} - +{% endblock content %} diff --git a/templates/admin/release_report_detail.html b/templates/admin/release_report_detail.html new file mode 100644 index 00000000..d1d45eef --- /dev/null +++ b/templates/admin/release_report_detail.html @@ -0,0 +1,139 @@ +{% extends "admin/library_report_base.html" %} +{% load humanize avatar_tags %} +{% block content %} + {% with bg_color='bg-gradient-to-tr from-[#7ac3e6]/50 to-[#d9b05e]/50' %} +
    +
    +
    +

    Boost

    +
    {{ commit_count|intcomma }} commits up through {{ version.display_name }}
    +
    +
    There were {{ version_commit_count|intcomma }} commits in {{ version.display_name }}
    +
    +
    +
    +

    Boost {{ version.display_name }}

    +
    {{ version_commit_count|intcomma }} Commits Across {{ library_count }} Repositories
    +
    +
    +
    + {% for author in top_contributors_release_overall %} +
    + {% avatar commitauthor=author %} +
    +
    + {{ author.name }} +
    +
    ({{ author.commit_count }})
    +
    +
    + {% endfor %} +
    +
    +
    +
    +
    +

    Most Committed Libraries

    +
    +
    +
    + {% for item in library_data %} +
    +
    +

    {{ item.library.name }}

    +
    {{ item.library.description }}
    +
    +
    +

    There were {{ item.version_count.commit_count }} commits in release {{ version.display_name }}

    + {% if item.new_contributors_count.count > 1 %} +
    + There were {{ item.new_contributors_count.count }} new contributors this release! +
    + {% elif item.new_contributors_count.count == 1 %} +
    + There was {{ item.new_contributors_count.count }} new contributor this release! +
    + {% endif %} +
    +
    Top Contributors
    +
    + {% for author in item.top_contributors_release %} +
    + {% avatar commitauthor=author %} +
    +
    + {{ author.name }} +
    +
    ({{ author.commit_count }})
    +
    +
    + {% endfor %} +
    +
    +
    +
    + {% endfor %} +
    +
    This is the last page
    + + {% endwith %} +{% endblock content %}