Skip to content

Commit fcdd30d

Browse files
added statistic chart to repo detail page (OWASP-BLT#3913)
* added stats chart to repo detail page * added the percentage sign after the percentage changes on statistic chart section --------- Co-authored-by: DonnieBLT <128622481+DonnieBLT@users.noreply.github.com>
1 parent 5b106aa commit fcdd30d

File tree

3 files changed

+440
-0
lines changed

3 files changed

+440
-0
lines changed

website/templates/projects/repo_detail.html

+209
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{% extends "base.html" %}
22
{% load humanize %}
33
{% load static %}
4+
{% load custom_filters %}
45
{% block title %}
56
{{ repo.name }} - Repo Details
67
{% endblock title %}
@@ -405,6 +406,119 @@ <h3 class="text-lg font-semibold text-gray-900">Commits</h3>
405406
</div>
406407
</div>
407408
</section>
409+
<!-- Statistics Charts -->
410+
<section class="bg-white py-6 px-4 rounded-lg shadow-lg">
411+
<h2 class="text-pink-500 font-bold text-lg">{{ repo.contributor_count|intcomma }} Contributions in the Last 30 Days</h2>
412+
<div class="grid grid-cols-3 gap-2 mt-4">
413+
<div class="p-4 border rounded-lg">
414+
<span class="text-blue-500 text-sm flex w-full items-center justify-start">
415+
<p class="mr-2" id="open-closed-issue-ratio"></p>
416+
<p>Opened/Closed Issue Ratio</p>
417+
</span>
418+
<span class="text-xs flex justify-between items-center mt-3">
419+
<p class="text-gray-400">{{ issue_ratio_change|floatformat:2 }} ({{ issue_ratio_percentage_change|floatformat:2 }}%)</p>
420+
<p class="{% if issue_ratio_change >= 0 %}text-green-500{% else %}text-red-500{% endif %} flex items-center">
421+
{% if issue_ratio_change >= 0 %}
422+
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
423+
<path fill-rule="evenodd" d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z" clip-rule="evenodd" />
424+
</svg>
425+
{% else %}
426+
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
427+
<path fill-rule="evenodd" d="M16.707 10.293a1 1 0 010 1.414l-6 6a1 1 0 01-1.414 0l-6-6a1 1 0 111.414-1.414L9 14.586V3a1 1 0 012 0v11.586l4.293-4.293a1 1 0 011.414 0z" clip-rule="evenodd" />
428+
</svg>
429+
{% endif %}
430+
past month
431+
</p>
432+
</span>
433+
</div>
434+
<div class="p-4 border rounded-lg">
435+
<span class="text-purple-500 text-sm flex w-full items-center justify-start">
436+
<p class="mr-2">{{ repo.open_pull_requests|intcomma }}</p>
437+
<p>Pull Requests Opened</p>
438+
</span>
439+
<span class="text-xs flex justify-between items-center mt-3">
440+
<p class="text-gray-400">{{ pr_change|intcomma }} ({{ pr_percentage_change|floatformat:2 }}%)</p>
441+
<p class="{% if pr_change >= 0 %}text-green-500{% else %}text-red-500{% endif %} flex items-center">
442+
{% if pr_change >= 0 %}
443+
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
444+
<path fill-rule="evenodd" d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z" clip-rule="evenodd" />
445+
</svg>
446+
{% else %}
447+
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
448+
<path fill-rule="evenodd" d="M16.707 10.293a1 1 0 010 1.414l-6 6a1 1 0 01-1.414 0l-6-6a1 1 0 111.414-1.414L9 14.586V3a1 1 0 012 0v11.586l4.293-4.293a1 1 0 011.414 0z" clip-rule="evenodd" />
449+
</svg>
450+
{% endif %}
451+
past month
452+
</p>
453+
</span>
454+
</div>
455+
<div class="p-4 border rounded-lg">
456+
<span class="text-blue-500 text-sm flex w-full items-center justify-start">
457+
<p class="mr-2">{{ repo.commit_count|intcomma }}</p>
458+
<p>Commits</p>
459+
</span>
460+
<span class="text-xs flex justify-between items-center mt-3">
461+
<p class="text-gray-400">{{ commit_change|intcomma }} ({{ commit_percentage_change|floatformat:2 }}%)</p>
462+
<p class="{% if commit_change >= 0 %}text-green-500{% else %}text-red-500{% endif %} flex items-center">
463+
{% if commit_change >= 0 %}
464+
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
465+
<path fill-rule="evenodd" d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z" clip-rule="evenodd" />
466+
</svg>
467+
{% else %}
468+
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
469+
<path fill-rule="evenodd" d="M16.707 10.293a1 1 0 010 1.414l-6 6a1 1 0 01-1.414 0l-6-6a1 1 0 111.414-1.414L9 14.586V3a1 1 0 012 0v11.586l4.293-4.293a1 1 0 011.414 0z" clip-rule="evenodd" />
470+
</svg>
471+
{% endif %}
472+
past month
473+
</p>
474+
</span>
475+
</div>
476+
</div>
477+
<div class="grid grid-cols-3 gap-4 mt-6">
478+
<div class="p-4 border rounded-lg">
479+
<div class="flex items-center justify-start">
480+
<span class="p-2 rounded-full mr-2">
481+
<svg class="w-5 h-5 text-blue-500"
482+
fill="none"
483+
stroke="currentColor"
484+
viewBox="0 0 24 24">
485+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
486+
</svg>
487+
</span>
488+
<p class="text-blue-500">Issues</p>
489+
</div>
490+
<canvas id="issuesChart"></canvas>
491+
</div>
492+
<div class="p-4 border rounded-lg">
493+
<div class="flex items-center justify-start">
494+
<span class="p-2 rounded-full">
495+
<svg class="w-5 h-5 text-purple-500"
496+
fill="none"
497+
stroke="currentColor"
498+
viewBox="0 0 24 24">
499+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
500+
</svg>
501+
</span>
502+
<p class="text-purple-500">Pull Requests</p>
503+
</div>
504+
<canvas id="prChart"></canvas>
505+
</div>
506+
<div class="p-4 border rounded-lg">
507+
<div class="flex items-center justify-start">
508+
<span class="p-2 rounded-full">
509+
<svg class="w-5 h-5 text-orange-500"
510+
fill="none"
511+
stroke="currentColor"
512+
viewBox="0 0 24 24">
513+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
514+
</svg>
515+
</span>
516+
<p class="text-orange-500">Pushes & Commits</p>
517+
</div>
518+
<canvas id="commitsChart"></canvas>
519+
</div>
520+
</div>
521+
</section>
408522
<!-- Community Section -->
409523
<section class="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden">
410524
<div class="p-6">
@@ -1258,5 +1372,100 @@ <h3 class="text-lg font-semibold text-gray-900">Issues with Bounties</h3>
12581372
}
12591373
});
12601374
</script>
1375+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
1376+
<script>
1377+
const open_issues = {{repo.open_issues}};
1378+
const closed_issues = {{repo.closed_issues}};
1379+
1380+
const open_closed_issue_ratio = (open_issues / closed_issues).toFixed(2);
1381+
const open_closed_issue_ratio_elem = document.getElementById("open-closed-issue-ratio");
1382+
open_closed_issue_ratio_elem.innerHTML = open_closed_issue_ratio;
1383+
</script>
1384+
<script>
1385+
const issuesCtx = document.getElementById('issuesChart').getContext('2d');
1386+
1387+
new Chart(issuesCtx, {
1388+
type: 'bar',
1389+
data: {
1390+
labels: {{ issues_labels|to_json }},
1391+
datasets: [{
1392+
label: 'Opened',
1393+
data: {{ issues_opened|to_json }},
1394+
backgroundColor: 'rgba(59, 130, 246, 0.5)',
1395+
borderColor: 'rgba(59, 130, 246, 1)',
1396+
borderWidth: 1
1397+
}, {
1398+
label: 'Closed',
1399+
data: {{ issues_closed|to_json }},
1400+
backgroundColor: 'rgba(29, 78, 216, 0.5)',
1401+
borderColor: 'rgba(29, 78, 216, 1)',
1402+
borderWidth: 1
1403+
}]
1404+
},
1405+
options: {
1406+
scales: {
1407+
x: {
1408+
display: false
1409+
}
1410+
}
1411+
},
1412+
});
1413+
1414+
const prCtx = document.getElementById('prChart').getContext('2d');
1415+
new Chart(prCtx, {
1416+
type: 'bar',
1417+
data: {
1418+
labels: {{ pr_labels|to_json }},
1419+
datasets: [{
1420+
label: 'Opened',
1421+
data: {{ pr_opened_data|to_json }},
1422+
backgroundColor: 'rgba(192, 38, 211, 0.5)',
1423+
borderColor: 'rgba(192, 38, 211, 1)',
1424+
borderWidth: 1
1425+
}, {
1426+
label: 'Closed',
1427+
data: {{ pr_closed_data|to_json }},
1428+
backgroundColor: 'rgba(109, 40, 217, 0.5)',
1429+
borderColor: 'rgba(109, 40, 217, 1)',
1430+
borderWidth: 1
1431+
}]
1432+
},
1433+
options: {
1434+
scales: {
1435+
x: {
1436+
display: false
1437+
}
1438+
}
1439+
},
1440+
});
1441+
1442+
const commitsCtx = document.getElementById('commitsChart').getContext('2d');
1443+
new Chart(commitsCtx, {
1444+
type: 'bar',
1445+
data: {
1446+
labels: {{ commits_labels|to_json }},
1447+
datasets: [{
1448+
label: 'Pushes',
1449+
data: {{ pushes_data }},
1450+
backgroundColor: 'rgba(234, 88, 12, 0.5)',
1451+
borderColor: 'rgba(234, 88, 12, 1)',
1452+
borderWidth: 1
1453+
}, {
1454+
label: 'Commits',
1455+
data: {{ commits_data|to_json }},
1456+
backgroundColor: 'rgba(194, 65, 12, 0.5)',
1457+
borderColor: 'rgba(194, 65, 12, 1)',
1458+
borderWidth: 1
1459+
}]
1460+
},
1461+
options: {
1462+
scales: {
1463+
x: {
1464+
display: false
1465+
}
1466+
}
1467+
},
1468+
});
1469+
</script>
12611470
<script src="{% static 'js/repo_detail.js' %}"></script>
12621471
{% endblock after_js %}

website/templatetags/custom_filters.py

+9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
import json
2+
13
from django import template
4+
from django.core.serializers.json import DjangoJSONEncoder
5+
from django.utils.safestring import mark_safe
26

37
register = template.Library()
48

@@ -12,3 +16,8 @@ def get_item(dictionary, key):
1216
@register.filter
1317
def before_dot(value):
1418
return str(value).split(".")[0]
19+
20+
@register.filter(name="to_json", is_safe=True)
21+
def to_json(value):
22+
"""Convert Python object to JSON string"""
23+
return mark_safe(json.dumps(value, cls=DjangoJSONEncoder))

0 commit comments

Comments
 (0)