Skip to content

Commit 05dc483

Browse files
committed
Normalize/guard null
1 parent 2aa4308 commit 05dc483

File tree

2 files changed

+38
-25
lines changed

2 files changed

+38
-25
lines changed

website/static/js/gsoc_pr_report.js

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@ window.gsocData = {
66
reportData: []
77
};
88

9-
// Silent logger (replaces console.error)
10-
function safeLog() {}
9+
// Safe error logging
10+
function safeLog(message, ...args) {
11+
if (window.Sentry && typeof window.Sentry.captureMessage === 'function') {
12+
window.Sentry.captureMessage(message, { extra: { args } });
13+
}
14+
}
1115

1216
// Function to extract chart data (callable from both chart rendering and PDF generation)
1317
function getChartData() {
1418
let yearlyCategories = [];
1519
let yearlySeriesData = [];
1620
let topReposLabels = [];
1721
let topReposSeries = [];
18-
22+
1923
// Process Yearly Chart Data
2024
if (window.gsocData.yearlyChart) {
2125
if (Array.isArray(window.gsocData.yearlyChart) && window.gsocData.yearlyChart.length > 0) {
@@ -37,7 +41,7 @@ function getChartData() {
3741
}
3842
}
3943
}
40-
44+
4145
// Process Top Repos Data
4246
if (window.gsocData.topReposChart) {
4347
if (Array.isArray(window.gsocData.topReposChart)) {
@@ -59,7 +63,7 @@ function getChartData() {
5963
topReposSeries = window.gsocData.topReposChart.data.map(Number);
6064
}
6165
}
62-
66+
6367
return {
6468
yearlyCategories,
6569
yearlySeriesData,
@@ -74,15 +78,15 @@ function parseEscapedJSON(str) {
7478

7579
try {
7680
// First, unescape the string (convert \u0022 to ", etc.)
77-
const unescapedStr = str.replace(/\\u([0-9a-fA-F]{4})/g, (match, hex) =>
81+
const unescapedStr = str.replace(/\\u([0-9a-fA-F]{4})/g, (match, hex) =>
7882
String.fromCharCode(parseInt(hex, 16))
7983
);
8084
// Also handle other common escapes
8185
const finalStr = unescapedStr
8286
.replace(/\\"/g, '"')
8387
.replace(/\\'/g, "'")
8488
.replace(/\\\\/g, '\\');
85-
89+
8690
return JSON.parse(finalStr);
8791
} catch (err) {
8892
safeLog("JSON parse failed:", err, str);
@@ -143,7 +147,7 @@ document.addEventListener('DOMContentLoaded', function () {
143147

144148
// Get chart data
145149
const { yearlyCategories, yearlySeriesData, topReposLabels, topReposSeries } = getChartData();
146-
150+
147151
// Store the chart data globally for PDF generation
148152
window.chartData = {
149153
yearlyCategories,
@@ -156,7 +160,7 @@ document.addEventListener('DOMContentLoaded', function () {
156160
if (yearlyChartElement) {
157161
if (yearlyCategories.length > 0 && yearlySeriesData.length > 0) {
158162
const maxValue = Math.max(...yearlySeriesData);
159-
const yAxisMax = Math.ceil(maxValue * 1.1);
163+
const yAxisMax = Math.max(5, Math.ceil(maxValue * 1.1)); // minimum scale of 5
160164

161165
const isDarkMode = document.documentElement.classList.contains('dark');
162166
const textColor = isDarkMode ? '#E5E7EB' : '#374151';
@@ -476,28 +480,28 @@ function exportChartData() {
476480
// Helper function for fallback chart
477481
async function addFallbackChart(doc, categories, series, x, y) {
478482
if (!categories || !series || categories.length === 0) return;
479-
483+
480484
// Draw a simple bar chart representation
481485
const maxValue = Math.max(...series);
482486
const chartWidth = 150;
483487
const chartHeight = 50;
484488
const barWidth = chartWidth / categories.length;
485-
489+
486490
// Draw bars
487491
series.forEach((value, index) => {
488492
const barHeight = (value / maxValue) * chartHeight;
489493
const barX = x + (index * barWidth);
490494
const barY = y + chartHeight - barHeight;
491-
495+
492496
doc.setFillColor(220, 38, 38); // Red color
493497
doc.rect(barX, barY, barWidth - 2, barHeight, 'F');
494-
498+
495499
// Add value label
496500
doc.setFontSize(6);
497501
doc.setTextColor(0, 0, 0);
498502
doc.text(String(value), barX + (barWidth / 2), barY - 2, { align: 'center' });
499503
});
500-
504+
501505
// Draw X axis
502506
categories.forEach((label, index) => {
503507
const labelX = x + (index * barWidth) + (barWidth / 2);
@@ -529,6 +533,9 @@ async function downloadReport(event) {
529533
await loadScript('https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js', {
530534
integrity: 'sha512-BNaRQnYJYiPSqHHDb58B0yaPfCu+Wgds8Gp/gU33kqBtgNS4tSPHuGibyoeqMV/TJlSKda6FXzoEyYGjTe+vXA=='
531535
});
536+
if (!window.jspdf || !window.html2canvas) {
537+
throw new Error('Required libraries failed to load. Please check your connection or disable ad blockers.');
538+
}
532539
const { jsPDF } = window.jspdf;
533540
const doc = new jsPDF('p', 'mm', 'a4');
534541

@@ -657,7 +664,7 @@ async function downloadReport(event) {
657664

658665
doc.setFontSize(12);
659666
doc.setTextColor(0, 0, 0);
660-
doc.text(`GSOC ${year.year} (${year.total_prs || 0} PRs, ${year.repos?.length || 0} Repositories)`, 20, yPosition);
667+
doc.text(`GSOC ${String(year.year)} (${String(year.total_prs || 0)} PRs, ${String(year.repos?.length || 0)} Repositories)`, 20, yPosition);
661668
yPosition += 10;
662669

663670
if (year.repos && year.repos.length > 0) {
@@ -667,7 +674,7 @@ async function downloadReport(event) {
667674
doc.addPage();
668675
yPosition = 20;
669676
}
670-
doc.text(`• ${repo.repo__name || 'Unknown'}: ${repo.pr_count || 0} PRs (${repo.unique_contributors || 0} contributors)`, 25, yPosition);
677+
doc.text(`• ${String(repo.repo__name || 'Unknown')}: ${String(repo.pr_count || 0)} PRs (${String(repo.unique_contributors || 0)} contributors)`, 25, yPosition);
671678
yPosition += 7;
672679
});
673680
}
@@ -687,7 +694,8 @@ async function downloadReport(event) {
687694
}
688695

689696
// Save the PDF
690-
const fileName = `gsoc_pr_report_${window.gsocData.summary.start_year || ''}_${window.gsocData.summary.end_year || ''}_${new Date().toISOString().split('T')[0]}.pdf`;
697+
const fileName = `gsoc_pr_report_${window.gsocData.summary.start_year || 'unknown'}_${window.gsocData.summary.end_year || 'unknown'}_${new Date().toISOString().split('T')[0]}.pdf`;
698+
691699
doc.save(fileName);
692700

693701
// Restore button state
@@ -883,7 +891,7 @@ function downloadHTMLReport() {
883891
const url = URL.createObjectURL(blob);
884892
const a = document.createElement('a');
885893
a.href = url;
886-
a.download = `gsoc_pr_report_${escapeHtml(reportData.summary.start_year || '')}_${escapeHtml(reportData.summary.end_year || '')}_${new Date().toISOString().split('T')[0]}.html`;
894+
a.download = `gsoc_pr_report_${reportData.summary.start_year || 'unknown'}_${reportData.summary.end_year || 'unknown'}_${new Date().toISOString().split('T')[0]}.html`;
887895
document.body.appendChild(a);
888896
a.click();
889897
document.body.removeChild(a);

website/views/project.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2246,18 +2246,24 @@ def gsoc_pr_report(request):
22462246
total_prs = 0
22472247
yearly_chart_data = []
22482248

2249+
def _repo_key(repo_row):
2250+
return repo_row.get("repo__name") or "Unknown repo"
2251+
2252+
def _repo_url(repo_row):
2253+
return repo_row.get("repo__repo_url") or ""
2254+
22492255
for year_block in report_data:
22502256
total_prs += year_block["total_prs"]
22512257
yearly_chart_data.append({"year": year_block["year"], "prs": year_block["total_prs"]})
22522258

22532259
for repo in year_block["repos"]:
2254-
all_repos.add(repo["repo__name"])
2260+
all_repos.add(_repo_key(repo))
22552261

22562262
# Top repos across all years
22572263
repo_totals = defaultdict(int)
22582264
for year_block in report_data:
22592265
for repo in year_block["repos"]:
2260-
repo_totals[repo["repo__name"]] += repo["pr_count"]
2266+
repo_totals[_repo_key(repo)] += repo["pr_count"]
22612267

22622268
top_repos_chart_data = [
22632269
{"repo": repo, "prs": count}
@@ -2279,14 +2285,13 @@ def gsoc_pr_report(request):
22792285
# Convert report_data into dict keyed by year
22802286
gsoc_data = {}
22812287
for entry in report_data:
2282-
repos_dict = {
2283-
repo["repo__name"]: {
2288+
repos_dict = {}
2289+
for repo in entry["repos"]:
2290+
repos_dict[_repo_key(repo)] = {
22842291
"count": repo["pr_count"],
2285-
"url": repo["repo__repo_url"],
2292+
"url": _repo_url(repo),
22862293
"contributors": repo["unique_contributors"],
22872294
}
2288-
for repo in entry["repos"]
2289-
}
22902295
gsoc_data[entry["year"]] = {"repos": repos_dict, "total_prs": entry["total_prs"]}
22912296

22922297
context = {

0 commit comments

Comments
 (0)