Files
website-v2/templates/admin/release_report_detail.html
2025-12-16 16:41:37 -05:00

831 lines
37 KiB
HTML

{% extends "admin/library_report_base.html" %}
{% load countries humanize avatar_tags text_helpers %}
{% load get_modulo_item_tag %}
{% load split_tag %}
{% load static %}
{% block css %}
{{ block.super }}
<style>
{% include "includes/_css_variables.css" %}
body {
-webkit-print-color-adjust:exact !important;
print-color-adjust:exact !important;
}
.link-icons a:hover {
color: var(--primary-color);
}
.sponsor-message ul > li {
list-style: circle;
margin-left: 2rem;
}
.sponsor-message a,
#library_index li a,
ul.slack-channels li div a,
.new-libraries a {
color: rgb(2, 132, 199);
}
.sponsor-message a:hover,
#library_index li a:hover,
ul.slack-channels li div a:hover,
.new-libraries a:hover {
color: rgb(255, 159, 0);
}
.committee_members img {
filter: grayscale(1);
}
#top-committed-libraries-chart .apexcharts-xaxis-label:nth-child(odd) {
transform: translateY(-8px);
}
#top-committed-libraries-chart .apexcharts-xaxis-label:nth-child(even) {
transform: translateY(8px);
}
.sponsor_message_copy p {
padding-top: 1em;
padding-bottom: 1em;
}
.sponsor_message_copy p:first-child {
padding-top: 0;
}
#search_stats_map {
border-radius: 8px;
}
</style>
{% endblock css %}
{% block content %}
<script src="{% static 'js/highlight.js' %}"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jsvectormap/dist/jsvectormap.min.css" />
<script src="https://cdn.jsdelivr.net/npm/jsvectormap"></script>
<script src="https://cdn.jsdelivr.net/npm/jsvectormap/dist/maps/world.js"></script>
{% now "F j, Y" as today %}
{% with bg_color='' %}
<div>
<div class="pdf-page grid grid-cols-2 gap-x-4 items-center justify-items-center {{ bg_color }}" style="background-image: url('{% static 'img/release_report/bg6.png' %}')">
<div class="flex flex-col h-full justify-between">
<div class="flex flex-col gap-y-2">
<h1 class="flex mb-0">
<img
class="mt-[3px]"
style="width:3.3rem; margin-right:.5rem;" src="{% static 'img/Boost_Symbol_Transparent.svg' %}"
>
<span class="font-bold">Boost</span>&nbsp;<span class="text-[2.75rem] self-end mb-[2px]">{{ report_configuration.display_name }}</span>
</h1>
<div class="flex gap-x-12 link-icons my-4 text-2xl justify-between">
{% include "includes/_social_icon_links.html" %}
</div>
<div class="mb-2" class="font-bold">{% firstof version.release_date today %}</div>
<div><span class="font-bold">{{ commit_count|intcomma }}</span> commit{{ commit_count|pluralize }} up through {{ report_configuration.display_name }}</div>
<div><span class="font-bold">{{ lines_added|intcomma }}</span> line{{ lines_added|pluralize }} added, <span class="font-bold">{{ lines_removed|intcomma }}</span> line{{ lines_removed|pluralize }} removed</div>
<div><span class="font-bold">{{ version_commit_count|intcomma }}</span> new commit{{ version_commit_count|pluralize }} in all library repositories</div>
<div><span class="font-bold">{{ commit_contributors_release_count }}</span> commit contributors, <span class="font-bold">{{ commit_contributors_new_count }}</span> new</div>
<div><span class="font-bold">{{ opened_issues_count|intcomma }}</span> issues opened, <span class="font-bold">{{ closed_issues_count|intcomma }}</span> closed</div>
<div><span class="font-bold">{{ mailinglist_total|intcomma }}</span> posts to the developer's mailing list</div>
<div><span class="font-bold">{{ mailinglist_contributor_release_count }}</span> unique mailing list users, <span class="font-bold">{{ mailinglist_contributor_new_count }}</span> new</div>
{% if added_library_count %}
<div><span class="font-bold">{{ added_library_count }}</span> librar{{ added_library_count|pluralize:"y,ies" }} added</div>
{% endif %}
{% if removed_library_count %}
<div><span class="font-bold">{{ removed_library_count }}</span> librar{{ removed_library_count|pluralize:"y,ies" }} removed</div>
{% endif %}
</div>
{% if downloads %}
<table class="p-0 m-0 mt-1 w-full text-xs text-left border-0 border-separate table-auto border-spacing-0 text-slate">
<tbody>
<tr>
<th scope="col"
colspan="2"
class="p-1 text-sm border border-b-0 border-gray-400 text-center">
Download Now!
</th>
</tr>
{% for os, download_files in downloads.items %}
{% for download in download_files %}
<tr>
{% if forloop.first %}
<th scope="row"
rowspan="{{ download_files|length }}"
class="p-2 h-14 whitespace-nowrap border border-r-0 {% if not forloop.parentloop.last %}border-b-0 {% endif %}border-gray-400 text-center">
<i class="fab fa-{% if os == 'Unix' %}linux{% else %}windows{% endif %}"></i> {{ os }}
</th>
{% endif %}
<td class="p-[.33rem] border {% if not forloop.last or not forloop.parentloop.last %}border-b-0 {% endif %}border-gray-400">
<a href="{{ download.url }}" class="text-sky-600 hover:text-orange">{{ download.display_name }}</a>
</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
<div class="flex flex-col h-full justify-between">
{% if report_configuration.release_report_cover_image and report_configuration.release_report_cover_image.url %}
<img
class="max-h-[60%]"
src="{{ report_configuration.release_report_cover_image.url|strip_query_string }}"
alt="release report cover image"
>
{% endif %}
{% if contribution_box_graph %}
<div class="flex flex-col gap-y-4">
<span class="mx-auto">Commits from {{ contribution_box_graph.graph_start }} to {{ contribution_box_graph.graph_end }}</span>
<div class="flex gap-x-1 mx-auto">
{% 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-[-10px] 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 mx-auto">
<div class="mr-1"><span class="font-bold">0</span> 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"><span class="font-bold">{{ contribution_box_graph.max }}</span> commits</div>
</div>
</div>
{% endif %}
</div>
</div>
{% if report_configuration.sponsor_message %}
<div id="fiscal_committee_page" class="pdf-page !p-8 {{ bg_color }} bg-gray-200 relative">
<h2 class="mt-0">From the Fiscal Sponsorship Committee</h2>
<div class="flex flex-col w-full h-[85%] sponsor-message relative">
<svg
class="absolute overflow-visible pointer-events-none z-10"
version="1.1"
id="bubble_background"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
>
<path
id="bubble"
style="fill:#ffffff;fill-rule:evenodd;stroke-width:0.896533"
d="M 28.068153,0 H 471.93339 c 17.18711,0 28.0676,10.63409 28.0674,26.12903 l -8e-4,56.64968 c 0,16.63468 -9.6004,26.12907 -28.0674,26.12907 -144.5008,3.63566 -356.11545,0.75984 -445.23187,0 -15.3824,0 -26.70095,-10.79719 -26.70074,-26.12907 l 7.8e-4,-56.64968 c 1.8e-4,-13.27403 12.64918,-26.12903 28.06737,-26.12903 z"
/>
</svg>
<div class="sponsor_message_copy dynamic-text p-[30px] mr-24 absolute z-20 max-h-[400px]">{{ report_configuration.sponsor_message|safe|linebreaks }}</div>
<div class="committee_members flex flex-wrap mt-2 text-sm text-center absolute" style="">
{% for user in committee_members|dictsort:"display_name" %}
<figure class="w-32 m-2">
{% avatar user=user is_link=False image_size="w-32 h-32" icon_size="text-8xl" use_user_hq_image=True %}
<figcaption class="p-1">{{user.display_name}}</figcaption>
</figure>
{% endfor %}
</div>
</div>
</div>
{% endif %}
<div class="flex flex-col pdf-page items-center justify-items-center {{ bg_color }}" style="background-image: url('{% static 'img/release_report/bg3.png' %}')">
<div class="flex flex-col mb-4">
<h2 class="mx-auto my-1">Git activity for this release</h2>
<div class="mx-auto"><span class="font-bold">{{ version_commit_count|intcomma }}</span> Commit{{ version_commit_count|pluralize }} Across <span class="font-bold">{{ library_count }}</span> Repositories</div>
{% if global_contributors_new_count %}
<div class="mx-auto"><span class="font-bold">{{ global_contributors_new_count|intcomma }}</span> First-time Contributor{{ global_contributors_new_count|pluralize }}</div>
{% endif %}
</div>
<div class="flex gap-x-8 justify-around w-full">
<div class="px-4">
<div class="mx-auto mb-6">Top Contributors</div>
<div class="m-auto grid grid-cols-1 gap-2">
{% for author in top_contributors_release_overall %}
<div class="flex flex-row gap-y-2 w-40 items-center">
{% avatar commitauthor=author %}
<div class="w-full flex flex-col ml-2">
<div class="text-[0.8rem] font-semibold overflow-ellipsis overflow-hidden whitespace-nowrap w-full">
{{ author.display_name }}
</div>
<div class="text-[0.7rem]"><span class="font-bold">{{ author.commit_count }}</span> commit{{ author.commit_count|pluralize }}</div>
</div>
</div>
{% endfor %}
</div>
</div>
<div class="flex flex-col justify-center">
<h3 class="mx-auto">Most Committed Libraries</h3>
<div id="top-committed-libraries-chart" class="w-full text-center"></div>
</div>
</div>
</div>
<div class="pdf-page flex {{ bg_color }}" style="background-image: url('{% static 'img/release_report/bg6.png' %}');">
<div class="flex gap-x-8 w-full">
<div class="px-4">
<div class="mx-auto mb-6 mt-[4.75rem]">Top Contributors</div>
<div class="m-auto grid grid-cols-1 gap-2">
{% for item in mailinglist_counts %}
<div class="flex flex-row gap-y-2 w-40 items-center">
{% avatar commitauthor=item %}
<div class="w-full flex flex-col ml-2">
<div class="text-[0.8rem] font-semibold overflow-ellipsis overflow-hidden whitespace-nowrap w-full">
{{ item.display_name|default:item.name }}
</div>
<div class="text-[0.7rem]"><span class="font-bold">{{ item.total_count }}</span> post{{ item.total_count|pluralize }}</div>
</div>
</div>
{% endfor %}
</div>
</div>
<div class="flex flex-col">
<h2 class="mx-auto">Mailing List</h2>
<div class="mx-auto mb-4">
There were
{% if mailinglist_total %}
<span class="font-bold">{{ mailinglist_total|intcomma }}</span>
{% else %}
no
{% endif %}
mailing list post{{ mailinglist_total|pluralize }} in version&nbsp;{{ report_configuration.display_name }} and
<span class="font-bold">{{ mailinglist_contributor_release_count }}</span>
poster{{ mailinglist_contributor_release_count|pluralize }}
in this version. (<span class="font-bold">{{ mailinglist_contributor_new_count }}</span> New)
</div>
<div class="text-center my-2">Weekly mailing list posts from {{prior_version.release_date}} to {% firstof version.release_date today %} on the Boost Developers mailing list.</div>
<div id="release_post_stats" class="mt-10 mx-4"></div>
</div>
</div>
</div>
<div class="pdf-page flex {{ bg_color }}" style="background-image: url('{% static 'img/release_report/bg6.png' %}');">
<div class="flex flex-col h-full mx-auto w-full">
<h2 class="mx-auto mb-10">Mailing List New Subscribers</h2>
<div class="text-center my-2">Mailing list new subscribers from from {{prior_version.release_date}} to {% firstof version.release_date today %} on the Boost Developers mailing list.</div>
<div id="subscriptions_stats"></div>
</div>
</div>
<div class="pdf-page flex {{ bg_color }}" style="background-image: url('{% static 'img/release_report/bg6.png' %}');">
<div class="flex flex-col h-full mx-auto w-full">
<h2 class="mx-auto mb-10">Mailing List Top 200 Most Frequently Used Words</h2>
<div class="flex flex-col flex-wrap gap-x-4 gap-y-1 text-xs h-5/6">
{% for word in mailinglist_wordcloud_frequencies %}
<div class="max-w-[10rem]">
{{ word }}
</div>
{% endfor %}
</div>
</div>
</div>
{% if mailinglist_wordcloud_base64 %}
<div class="pdf-page flex {{ bg_color }}" style="background-image: url('{% static 'img/release_report/bg6.png' %}');">
<div class="flex flex-col mx-auto">
<h2 class="mx-auto mb-10">Mailing List Word Cloud</h2>
<div class="flex mx-auto">
<img src="data:image/png;base64,{{ mailinglist_wordcloud_base64 }}" alt="Mailing List Word Cloud" class="w-full">
</div>
</div>
</div>
{% endif %}
{% if slack %}
{% for slack_channel_group in slack_channels %}
<div class="pdf-page flex items-center justify-items-center {{ bg_color }}" style="background-image: url('{% static 'img/release_report/bg4-rev.png' %}');">
<div class="flex flex-col h-full mx-auto w-full">
{% if forloop.first %}
<h2 class="mx-auto">Slack Channels</h2>
{% endif %}
<ul class="flex flex-row flex-wrap mx-auto w-full flex-wrap text-sm slack-channels">
{% for channel in slack_channel_group %}
<li class="w-1/2 p-2">
<strong>#{{channel.name}}</strong>
{% if channel.purpose %}
<div class="mx-auto">{{channel.purpose|safe}}</div>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endfor %}
{% for slack_group in slack %}
<div class="pdf-page flex justify-items-center {{ bg_color }}" style="background-image: url('{% static 'img/release_report/bg4-rev.png' %}');">
<div class="flex flex-col mx-auto">
<div class="flex gap-x-[6rem]">
{% for slack_item in slack_group %}
<div class="flex flex-col mx-auto gap-y-2 w-96">
<h2 class="my-2">#{{slack_item.channel.name}}</h2>
<div>
<div class="mx-auto">
<span class="font-bold">{{ slack_item.total|intcomma }}</span>
slack message{{ slack_item.total|pluralize }} in #{{ slack_item.channel.name }}
</div>
<div class="mx-auto">
<span class="font-bold">{{ slack_item.user_count }}</span>
{{ slack_item.user_count|pluralize:"person,people" }}
were active for this release. (<span class="font-bold">{{ slack_item.new_user_count }}</span> New)
</div>
</div>
<div class="flex gap-x-2">
<div>
<div class="font-bold">
Top Contributors
</div>
<div class="m-auto grid grid-cols-1 gap-2 mt-4">
{% for item in slack_item.users %}
<div class="flex flex-row gap-y-2 w-40 items-center">
{% 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] font-semibold overflow-ellipsis overflow-hidden whitespace-nowrap w-full">
{{ item.real_name }}
</div>
<div class="text-[0.7rem]"><span class="font-bold">{{ item.total }}</span> message{{ item.total|pluralize }}</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
{% endif %}
{% if search_wordcloud_base64 %}
<div class="pdf-page flex flex-col {{ bg_color }}" style="background-image: url('{% static 'img/release_report/bg3.png' %}');">
<h2 class="mx-auto mb-10">Website Searches</h2>
<div class="flex flex-col mx-auto">
<div class="flex flex-row w-full h-1/2 p-4">
<div id="search_stats_map" class="w-1/2"></div>
<div class="w-1/2">
<div><strong>{{ search_stats.total_searches }}</strong> website searches.</div>
<div class="my-2">
<div class="py-1">Top <strong>{{search_stats.top_countries|length}}</strong> countries:</div>
<ul>
{% for code, count in search_stats.top_countries %}
{% get_country code as country %}
<li>{{ country.name }}: <strong>{{ count }}</strong> search{{ count|pluralize:"es" }}</li>
{% endfor %}
</ul>
</div>
</div>
</div>
<div class="w-full h-1/2 p-4">
<div class="flex mx-auto">
<img src="data:image/png;base64,{{ search_wordcloud_base64 }}" alt="Search Word Cloud" class="w-full">
</div>
</div>
</div>
</div>
{% endif %}
<div class="pdf-page flex items-center justify-items-center {{ bg_color }}" style="background-image: url('{% static 'img/release_report/bg6-rev.png' %}');">
<div class="flex flex-col h-full mx-auto w-full">
<h2 class="mx-auto">Updated Libraries</h2>
<ul id="library_index" class="flex flex-col flex-wrap gap-x-4 gap-y-2 text-xs h-5/6">
{% for library, in_version_library_data in library_index_libraries %}
{% if in_version_library_data %}
<li class="max-w-[10rem] flex gap-x-1 items-center">
{% if library.group == "great" %}
<i class="text-orange fa-solid fa-star"></i>
{% elif library.group == "good" %}
<i class="text-orange fa-regular fa-star"></i>
{% else %}
<i class="text-orange fa-regular fa-fw"></i>
{% endif %}
<a href="#library-{{library.display_name}}" class="font-semibold">{{ library.name }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
</div>
<div class="pdf-page flex items-center justify-items-center {{ bg_color }}" style="background-image: url('{% static 'img/release_report/bg6-rev.png' %}');">
<div class="flex flex-col h-full mx-auto w-full">
<h2 class="mx-auto">Other Libraries</h2>
<ul id="library_index" class="flex flex-col flex-wrap gap-x-4 gap-y-2 text-xs h-5/6">
{% for library, in_version_library_data in library_index_libraries %}
{% if not in_version_library_data %}
<li class="max-w-[10rem] flex gap-x-1 items-center">
{% if library.group == "great" %}
<i class="text-orange fa-solid fa-star"></i>
{% elif library.group == "good" %}
<i class="text-orange fa-regular fa-star"></i>
{% else %}
<i class="text-orange fa-regular fa-fw"></i>
{% endif %}
<span>{{ library.name }}</span>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
</div>
{% if new_libraries %}
<div class="pdf-page flex items-center justify-items-center {{ bg_color }}" style="background-image: url('{% static 'img/release_report/bg3.png' %}');">
<div class="flex flex-col h-full mx-auto w-full">
<h2 class="mx-auto">New Libraries</h2>
{% for library in new_libraries %}
<div class="flex flex-row gap-x-4 mb-4 gap-y-2 new-libraries">
{% if library.graphic %}
<div class="max-w-[16rem]">
<img src="{{ library.graphic.url }}" alt="" />
</div>
{% endif %}
<div class="flex flex-col gap-y-2">
<div class="font-semibold text-lg">
{{ library.name }}
</div>
<div class="text-sm">
{{ library.description }}
</div>
<div class="text-sm">
<a href="{{ library.github_url }}">{{ library.github_url }}</a>
</div>
<div class="text-sm font-semibold">Contributors:</div>
<div class="flex flex-row flex-wrap gap-x-4 gap-y-2">
{% for author in library.authors.all %}
<div class="flex flex-row gap-y-2 w-30 items-center">
{% avatar user=author %}
<div class="w-full flex flex-col pl-2">
<div class="text-[0.8rem] font-semibold overflow-ellipsis overflow-hidden whitespace-nowrap w-full">
{{ author.display_name }}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% with "img/release_report/bg1.png,img/release_report/bg4-rev.png,img/release_report/bg3-rev.png,img/release_report/bg1-rev.png,img/release_report/bg2.png,img/release_report/bg6-rev.png,img/release_report/bg3.png,img/release_report/bg4.png,img/release_report/bg5.png,img/release_report/bg6.png,img/release_report/bg2-rev.png" as bg_list_str %}
{% with bg_list=bg_list_str|split:"," %}
{% for batch in batched_library_data %}
{% with current_bg=bg_list|get_modulo_item:forloop.counter0 %}
<div class="pdf-page flex flex-col {{ bg_color }}" style="background-image: url('{% static current_bg %}')">
{% for item in batch %}
<div class="grid grid-cols-3 gap-x-8 w-full p-4 h-1/2" id="library-{{ item.library.display_name }}">
<div class="col-span-2 flex flex-col gap-y-4">
<div class="flex flex-col gap-y-4">
<h2 class="text-orange mb-1 mt-0">{{ item.library.display_name }}</h2>
<div>{{ item.library.description }}</div>
</div>
<div class="flex gap-x-8 items-center">
{% if item.library.graphic %}
<div class="max-w-[10rem]">
<img src="{{ item.library.graphic.url }}" alt="" />
</div>
{% endif %}
<div class="flex flex-col gap-y-1">
<div>
There
{{ item.version_count.commit_count|pluralize:"was,were" }}
<span class="font-bold">{{ item.version_count.commit_count }}</span>
commit{{ item.version_count.commit_count|pluralize }}
in release {{ report_configuration.display_name }}
</div>
{% with insertions=item.library_version.insertions deletions=item.library_version.deletions %}
<div>
<span class="font-bold">{{ insertions|intcomma }}</span> line{{ insertions|pluralize }} added, <span class="font-bold">{{ deletions|intcomma }}</span> line{{ deletions|pluralize }} removed
</div>
{% endwith %}
{% with count=item.new_contributors_count.count %}
{% if count >= 1 %}
<div>
There {{ count|pluralize:"was,were" }} <span class="font-bold">{{ count }}</span> new contributor{{ count|pluralize }} for this release!
</div>
{% endif %}
{% endwith %}
<div>
There {{ item.issues.opened|pluralize:"was,were" }} <span class="font-bold">{{ item.issues.opened }}</span> issue{{ item.issues.opened|pluralize }} opened
and {{ item.issues.closed|pluralize:"was,were" }} <span class="font-bold">{{ item.issues.closed }}</span> issue{{ item.issues.closed|pluralize }} closed
</div>
{% if item.deps.added or item.deps.removed %}
<div>
There {{ item.deps.added|length|pluralize:"was,were" }} <span class="font-bold">{{ item.deps.added|length }}</span> dependenc{{ item.deps.added|length|pluralize:"y,ies" }} added
and
<span class="font-bold">{{ item.deps.removed|length }}</span> dependenc{{ item.deps.removed|length|pluralize:"y,ies" }} removed
</div>
{% endif %}
</div>
</div>
</div>
<div class="px-4">
<div class="mx-auto mb-6">Top Contributors</div>
<div class="m-auto grid grid-cols-1 gap-2">
{% for author in item.top_contributors_release %}
<div class="flex flex-row gap-y-2 w-40 items-center">
{% avatar commitauthor=author %}
<div class="w-full flex flex-col ml-2">
<div class="text-[0.8rem] font-semibold overflow-ellipsis overflow-hidden whitespace-nowrap w-full">
{{ author.display_name }}
</div>
<div class="text-[0.7rem]"><span class="font-bold">{{ author.commit_count }}</span> commit{{ author.commit_count|pluralize }}</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>
{% endwith %}
{% endfor %}
{% endwith %}
{% endwith %}
</div>
<script>
const generalGraphOptions = {
chart: {
height: 400,
type: 'bar',
foreColor: '#373d3f',
background: '#ffffff00',
toolbar: {
show: false,
},
zoom: {
enabled: false
},
},
plotOptions: {
bar: {
borderRadius: 2,
dataLabels: {
position: 'top', // top, center, bottom
},
}
},
dataLabels: {
offsetY: -16,
enabled: true,
style: {
fontSize: '11px',
colors: ["rgb(49, 74, 87)"],
}
},
xaxis: {
position: 'bottom',
axisBorder: {
show: false
},
axisTicks: {
show: false
},
tooltip: {
enabled: true,
}
},
yaxis: {
axisBorder: {
show: true
},
axisTicks: {
show: true,
},
labels: {
show: true,
}
},
};
var committedLibrariesOptions = {
...generalGraphOptions,
series: [{
name: 'Commits',
data: [{% for library in top_libraries_for_version|slice:":5" %}{{library.commit_count}}, {% endfor %}]
}],
xaxis: {
type: "category",
categories: [{% for library in top_libraries_for_version|slice:":5" %} "{{ library.name }}", {% endfor %}],
labels: {
rotate: 0,
hideOverlappingLabels: false
}
},
};
const libraryCommitsChart = new ApexCharts(document.querySelector("#top-committed-libraries-chart"), committedLibrariesOptions);
libraryCommitsChart.render();
var mailingListPostsOptions = {
...generalGraphOptions,
series: [{
name: 'Posts',
data: {{ mailinglist_post_stats|safe }}
}],
axisTicks: {
show: true,
},
xaxis: {
type: "category",
title: {
text: "Calendar Week {{ mailinglist_charts_start_year }}"
},
labels: {
rotate: 0,
formatter: function (value) {
// Format is: "WEEK (YEAR)", e.g. "51 (24)"
const match = value.match(/^(\d+)\s+\((\d{2})\)$/);
if (match) {
const week = parseInt(match[1]);
const year = match[2];
return week === 1 ? `1 ('${year})` : `${week}`;
}
return value; // fallback if pattern doesn't match
}
}
},
yaxis: {
title: {
text: "Posts"
}
},
};
const mailingListPostsChart = new ApexCharts(document.querySelector("#release_post_stats"), mailingListPostsOptions);
mailingListPostsChart.render();
const newEntries = {{mailinglist_new_subscribers_stats|safe}};
var subscriptionsOptions = {
...generalGraphOptions,
series: [{
name: 'New Subscribers',
data: newEntries
}],
axisTicks: {
show: true,
},
xaxis: {
type: "numeric",
title: {
text: "Calendar Week {{ mailinglist_charts_start_year }}"
},
labels: {
rotate: 0,
formatter: function (value) {
// Format is: "WEEK (YEAR)", e.g. "51 (24)"
const match = value.match(/^(\d+)\s+\((\d{2})\)$/);
if (match) {
const week = parseInt(match[1]);
const year = match[2];
return week === 1 ? `1 (20${year})` : `${week}`;
}
return value; // fallback if pattern doesn't match
}
}
},
yaxis: {
title: {
text: "New Subscribers"
},
},
dataLabels: {
enabled: false
}
};
const subscriptionsChart = new ApexCharts(document.querySelector("#subscriptions_stats"), subscriptionsOptions);
subscriptionsChart.render();
// Use fitText to resize text to fit its container.
// Starts at MAX_FONT_SIZE and tries smaller sizes until it fits or hits MIN_FONT_SIZE.
const fitText = async () => {
for (const text of document.querySelectorAll('.dynamic-text')) {
const MAX_FONT_SIZE = 23;
const MIN_FONT_SIZE = 16;
// Start with a large font size
let fontSize = MAX_FONT_SIZE;
text.style.fontSize = fontSize + 'px';
var computedStyle = window.getComputedStyle(text);
const intRegex = /(\d+)/;
const paddingY = parseInt(
computedStyle.getPropertyValue('padding-top').match(intRegex)
) + parseInt(
computedStyle.getPropertyValue('padding-right').match(intRegex)
);
const paddingX = parseInt(
computedStyle.getPropertyValue('padding-bottom')
) + parseInt(
computedStyle.getPropertyValue('padding-left')
);
const usableHeight = text.clientHeight - paddingY;
const textHeight = text.scrollHeight - paddingY;
// Reduce font size until text fits container
while (textHeight > usableHeight && fontSize > MIN_FONT_SIZE) {
fontSize--;
text.style.fontSize = fontSize + 'px';
}
}
}
const adjustBubbleSize = (width, height, radius) => {
const bubbleSVG = document.querySelector('#bubble_background');
if (!bubbleSVG) return;
const bubblePath = document.querySelector('#bubble');
if (!bubblePath) return;
// Set SVG size and viewBox
const attrs = { width, height, viewBox: `0 0 ${width} ${height}` };
Object.entries(attrs).forEach(
([key, value]) => bubbleSVG.setAttribute(key, value)
);
bubblePath.setAttribute(
'd',
`M ${radius},0
H ${width - radius}
Q ${width},0 ${width},${radius}
V ${height - radius}
Q ${width},${height} ${width - radius},${height}
H ${radius}
Q 0,${height} 0,${height - radius}
V ${radius}
Q 0,0 ${radius},0 Z`
);
};
const drawBubble = async () => {
// determines the gap between the text and the sponsor avatars
const blockGap = 35;
/*
bubbleScalingAdjustment accounts for scaling of bubble with longer content, with
the bubble and the copy being different heights
*/
const bubbleScalingAdjustment = 1; // 1.15 when point path in use
const bubbleCommitteeSpacing = 10;
const copyTextAdjustment = -2;
const bubbleRadius = 25;
const cmtePage = document.querySelector(".sponsor-message");
const usableHeight = cmtePage.offsetHeight;
const sponsorMessage = document.querySelector('.sponsor_message_copy');
const committeeMembers = document.querySelector('.committee_members');
const bubbleSVG = document.querySelector('#bubble_background');
const msgHeight = sponsorMessage.offsetHeight;
const msgWidth = sponsorMessage.offsetWidth;
const datum = (usableHeight - (sponsorMessage.offsetHeight + committeeMembers.offsetHeight)) / 2;
sponsorMessage.style.top = `${datum + copyTextAdjustment}px`;
bubbleSVG.style.top = `${datum}px`;
committeeMembers.style.top = `${datum + blockGap + sponsorMessage.offsetHeight}px`;
adjustBubbleSize(msgWidth, msgHeight * bubbleScalingAdjustment, bubbleRadius);
};
const drawSearchStatsMap = async () => {
const countryData = {{ search_stats.country_stats|safe }};
const map = new jsVectorMap({
selector: '#search_stats_map',
map: 'world',
zoomButtons: false,
visualizeData: { scale: ['#e8f1ff', '#1a73e8'], values: countryData },
onRegionTooltipShow: function(event, tooltip, code) {
const countryName = tooltip.text();
const searchCount = countryData[code] || 0;
tooltip.text(`${countryName}: ${searchCount} searches`);
}
})
}
window.addEventListener('DOMContentLoaded', async () => {
await fitText();
await drawBubble();
await drawSearchStatsMap();
});
</script>
{% endwith %}
{% endblock content %}