Bauke/tildes-issue-log
Bauke
/
tildes-issue-log
Archived
1
Fork 0
This repository has been archived on 2022-10-04. You can view files and clone it, but cannot push or open issues or pull requests.
tildes-issue-log/source/scripts/statistics.ts

322 lines
9.1 KiB
TypeScript

import {promises as fsp} from 'fs';
import {join} from 'path';
import {getIssues, getCommits, getMergeRequests} from './download';
export interface Result {
count: number;
}
export interface MonthlyResult extends Result {
month: number;
year: number;
}
export interface Statistics {
contributions: {
authors: number;
commits: number;
};
issues: {
closed: number;
opened: number;
};
lines: {
added: number;
removed: number;
};
mergeRequests: {
closed: number;
opened: number;
};
month: number;
year: number;
}
async function entry(): Promise<void> {
const dataDirectory: string = join(__dirname, '../pages/data/');
await fsp.mkdir(dataDirectory, {recursive: true});
// Get the GitLab data from a local file or download them from the API.
const commits: any[] = await getCommits();
const issues: any[] = await getIssues();
const mergeRequests: any[] = await getMergeRequests();
// Get the amount of commits made for each month.
const commitsMade: MonthlyResult[] = await findData(
commits,
() => false,
(result: MonthlyResult, commit: any) =>
result.year === new Date(commit.created_at).getFullYear() &&
result.month === new Date(commit.created_at).getMonth() + 1,
(commit: any): MonthlyResult => ({
count: 1,
year: new Date(commit.created_at).getFullYear(),
month: new Date(commit.created_at).getMonth() + 1
}),
() => 1
);
// Get the amount of lines added for each month.
const linesAdded: MonthlyResult[] = await findData(
commits,
() => false,
(result: MonthlyResult, commit: any) =>
result.year === new Date(commit.created_at).getFullYear() &&
result.month === new Date(commit.created_at).getMonth() + 1,
(commit: any): MonthlyResult => ({
count: commit.stats.additions,
year: new Date(commit.created_at).getFullYear(),
month: new Date(commit.created_at).getMonth() + 1
}),
(commit: any): number => commit.stats.additions
);
// Get the amount of lines removed for each month.
const linesRemoved: MonthlyResult[] = await findData(
commits,
() => false,
(result: MonthlyResult, commit: any) =>
result.year === new Date(commit.created_at).getFullYear() &&
result.month === new Date(commit.created_at).getMonth() + 1,
(commit: any): MonthlyResult => ({
count: commit.stats.deletions,
year: new Date(commit.created_at).getFullYear(),
month: new Date(commit.created_at).getMonth() + 1
}),
(commit: any): number => commit.stats.deletions
);
// Get the amount of issues opened for each month.
const issuesOpened: MonthlyResult[] = await findData(
issues,
() => false,
(result: MonthlyResult, issue: any) =>
result.year === new Date(issue.created_at).getFullYear() &&
result.month === new Date(issue.created_at).getMonth() + 1,
(issue: any) => ({
count: 1,
month: new Date(issue.created_at).getMonth() + 1,
year: new Date(issue.created_at).getFullYear()
}),
() => 1
);
// Get the amount of issues closed for each month.
const issuesClosed: MonthlyResult[] = await findData(
issues,
(issue: any) => issue.closed_at === null,
(result: MonthlyResult, issue: any) =>
result.year === new Date(issue.closed_at).getFullYear() &&
result.month === new Date(issue.closed_at).getMonth() + 1,
(issue: any) => ({
count: 1,
month: new Date(issue.closed_at).getMonth() + 1,
year: new Date(issue.closed_at).getFullYear()
}),
() => 1
);
// Get the amount of merge requests opened for each month.
const mergeRequestsOpened: MonthlyResult[] = await findData(
mergeRequests,
() => false,
(result: MonthlyResult, mergeRequest: any) =>
result.year === new Date(mergeRequest.created_at).getFullYear() &&
result.month === new Date(mergeRequest.created_at).getMonth() + 1,
(mergeRequest: any) => ({
count: 1,
month: new Date(mergeRequest.created_at).getMonth() + 1,
year: new Date(mergeRequest.created_at).getFullYear()
}),
() => 1
);
// Get the amount of merge requests closed/merged for each month.
const mergeRequestsClosed: MonthlyResult[] = await findData(
mergeRequests,
(mergeRequest: any) =>
mergeRequest.closed_at === null && mergeRequest.merged_at === null,
(result: MonthlyResult, mergeRequest: any) =>
result.year ===
new Date(
mergeRequest.closed_at === null
? mergeRequest.merged_at
: mergeRequest.closed_at
).getFullYear() &&
result.month ===
new Date(
mergeRequest.closed_at === null
? mergeRequest.merged_at
: mergeRequest.closed_at
).getMonth() +
1,
(mergeRequest: any) => ({
count: 1,
month:
new Date(
mergeRequest.closed_at === null
? mergeRequest.merged_at
: mergeRequest.closed_at
).getMonth() + 1,
year: new Date(
mergeRequest.closed_at === null
? mergeRequest.merged_at
: mergeRequest.closed_at
).getFullYear()
}),
() => 1
);
// Create empty statistics for months that may not have any data.
const statistics: Statistics[] = [];
// Start at 2018 and end in the current year.
for (let year = 2018; year < new Date().getFullYear() + 1; year++) {
for (let month = 1; month < 13; month++) {
// Tildes started in April of 2018, so don't create anything unless we're in or
// after that month.
if (year === 2018 && month < 4) {
continue;
}
// Once we reach the current year and the next month, stop creating more data.
if (
year === new Date().getFullYear() &&
month === new Date().getMonth() + 2
) {
break;
}
statistics.push({
contributions: {
authors: 0,
commits: 0
},
issues: {
closed: 0,
opened: 0
},
lines: {
added: 0,
removed: 0
},
mergeRequests: {
closed: 0,
opened: 0
},
month,
year
});
}
}
for (const statistic of statistics) {
// Create a `Set()` that will hold all our unique author usernames.
const uniqueContributors: Set<string> = new Set();
for (const commit of commits) {
if (
statistic.year === new Date(commit.created_at).getFullYear() &&
statistic.month === new Date(commit.created_at).getMonth() + 1
) {
uniqueContributors.add(commit.author_name);
}
}
// Add all the data to the statistic.
statistic.contributions.authors = uniqueContributors.size;
statistic.issues.opened = await getResultCount(
issuesOpened,
statistic.year,
statistic.month
);
statistic.contributions.commits = await getResultCount(
commitsMade,
statistic.year,
statistic.month
);
statistic.issues.closed = await getResultCount(
issuesClosed,
statistic.year,
statistic.month
);
statistic.lines.added = await getResultCount(
linesAdded,
statistic.year,
statistic.month
);
statistic.lines.removed = await getResultCount(
linesRemoved,
statistic.year,
statistic.month
);
statistic.mergeRequests.closed = await getResultCount(
mergeRequestsClosed,
statistic.year,
statistic.month
);
statistic.mergeRequests.opened = await getResultCount(
mergeRequestsOpened,
statistic.year,
statistic.month
);
}
// Sort the statistics ascending by year and month.
statistics.sort((a, b) => a.year - b.year || a.month - b.month);
// Write the statistics to file.
await fsp.writeFile(
join(dataDirectory, 'statistics.json'),
JSON.stringify(statistics, null, 2)
);
}
// A generic function that takes in data and a set of functions to count something from
// that data.
export async function findData<T extends Result>(
data: any[],
ignoreFn: (point: any) => boolean,
findFn: (result: T, point: any) => boolean,
undefinedFn: (point: any) => T,
incrementFn: (point: any) => number
): Promise<T[]> {
const monthlies: T[] = [];
for (const point of data) {
// If the ignore function returns true we want to skip the current datapoint.
if (ignoreFn(point)) {
continue;
}
// Find the monthly that applies to this datapoint.
const monthly: T | undefined = monthlies.find(result =>
findFn(result, point)
);
// If it doesn't exist yet we want to create it.
// Otherwise we want to increment the count.
if (monthly === undefined) {
monthlies.push(undefinedFn(point));
} else {
monthly.count += incrementFn(point);
}
}
return monthlies;
}
// A function that returns the count of a MonthlyResult in the specified year and month.
async function getResultCount(
results: MonthlyResult[],
year: number,
month: number
): Promise<number> {
const result: MonthlyResult | undefined = results.find(
val => val.year === year && val.month === month
);
return result === undefined ? 0 : result.count;
}
if (require.main === module) {
entry();
}