\n';
statistics += ' Statistics
\n';
const commitStats = changedLines(commits);
const contributors = uniqueContributors(commits);
statistics += ` In the month of ${months[wantedMonth]}, `;
statistics += `${commits.length} commits were made by ${contributors.length} contributors, `;
statistics += `changing a total of ${Math.abs(commitStats.total)} (+${commitStats.added}|-${commitStats.deleted}) lines. `;
statistics += `${opened.length} issues were opened and `;
statistics += `${closed.length} issues were closed.
\n`;
statistics += ` An average of ${(opened.length / 30).toFixed(2)} issues were opened `;
statistics += `and ${(closed.length / 30).toFixed(2)} issues were closed each day.
\n`;
statistics += ` The average time to close issues was ${avgTime(closed, 'days')} days `;
statistics += `or ${avgTime(closed, 'hours')} hours.
\n`;
const topUsers = freqUsers(opened, 3);
statistics += ' Top 3 issue creators:
\n';
statistics += ' \n';
for (const user in topUsers) {
statistics += ' - \n';
statistics += ` ${user}`;
statistics += ' with ';
statistics += `${topUsers[user]} issues created.\n`;
statistics += '
\n';
}
statistics += '
\n';
let labels = labelsAlphabet(opened, true);
statistics += ' Amount of labels assigned to currently open issues:
\n';
statistics += ' \n';
for (const label in labels) {
statistics += ' - \n';
statistics += ` ${label}:`;
statistics += `${labels[label]} `;
if (labels[label] === 1) {
statistics += 'time.\n';
} else {
statistics += 'times.\n';
}
statistics += '
\n';
}
statistics += '
\n';
labels = labelsAlphabet(closed, false);
statistics += ' Amount of labels assigned to closed issues:
\n';
statistics += ' \n';
for (const label in labels) {
statistics += ' - \n';
statistics += ` ${label}:`;
statistics += `${labels[label]} `;
if (labels[label] === 1) {
statistics += 'time.\n';
} else {
statistics += 'times.\n';
}
statistics += '
\n';
}
statistics += '
\n';
statistics += '\n';
fs.writeFileSync(outIssuesPath + '_statistics.html', statistics, {encoding: 'UTF-8'});
resolve();
});
}
function createFeeds() {
const feed = new Feed({
title: 'Tildes Issue Log',
description: 'Monthly blog highlighting the changes of Tildes.net',
id: 'https://til.bauke.xyz',
link: 'https://til.bauke.xyz',
language: 'en',
image: 'https://til.bauke.xyz/android-chrome-192x192.png',
favicon: 'https://til.bauke.xyz/favicon.ico',
copyright: 'AGPL-3.0-or-later Tildes Issue Log Contributors https://gitlab.com/Bauke/tildes-issue-log',
generator: 'https://github.com/jpmonette/feed',
feedLinks: {
atom: 'https://til.bauke.xyz/feed.atom',
json: 'https://til.bauke.xyz/feed.json',
rss: 'https://til.bauke.xyz/feed.rss'
},
author: {
name: 'Bauke',
email: 'me@bauke.xyz',
link: 'https://bauke.xyz'
}
});
const posts = fs.readdirSync(path.join(paths.out, 'posts'));
// Sort the posts descending year and month
posts.sort((a, b) => {
const yearA = Number(a.replace(/\D/g, ''));
const yearB = Number(b.replace(/\D/g, ''));
if (yearA === yearB) {
const monthA = months.join(',').toLowerCase().split(',').indexOf(a.substring(0, a.indexOf('-'))) + 1;
const monthB = months.join(',').toLowerCase().split(',').indexOf(b.substring(0, b.indexOf('-'))) + 1;
return monthB - monthA;
}
return yearB - yearA;
});
for (const post of posts) {
// Skip the template, that doesn't need to be included
if (post.includes('template')) {
continue;
}
const html = fs.readFileSync(path.join(paths.out, 'posts', post), 'UTF8');
const $ = cheerio.load(html);
const title = $('#wrapper>h1').text();
const id = `https://til.bauke.xyz/posts/${post}`;
const date = new Date(Date.UTC(
Number(post.replace(/\D/g, '')),
// Add one to the month since UTC months are 0 based and since we set the
// day as 0 we'll get the Date back as the last day of the previous month
months.join(',').toLowerCase().split(',').indexOf(post.substring(0, post.indexOf('-'))) + 1,
0, 23, 59, 59
));
const content = $('#post')
.html()
.replace(/