Skip to content

Instantly share code, notes, and snippets.

@herpiko

herpiko/index.js Secret

Created November 15, 2021 05:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save herpiko/b5aa126e29f5f2ad15c4ff481bf5aee8 to your computer and use it in GitHub Desktop.
Save herpiko/b5aa126e29f5f2ad15c4ff481bf5aee8 to your computer and use it in GitHub Desktop.
gitlab project time tracking
const axios = require('axios');
const fs = require('fs');
const conf = {
host: 'https://somedomain.com',
token: 'TOKEN',
projectId: '2',
groupId: '1',
perPage: 100,
maxPage: 5,
};
var closedIssuesWithMR = [];
const getIssues = (page) => {
let url =
conf.host +
`/api/v4/projects/` +
conf.projectId +
`/issues?per_page=` +
conf.perPage +
`&page=` +
page +
`&order_by=updated_at&state=closed`;
return axios
.get(url, { headers: { 'PRIVATE-TOKEN': conf.token } })
.then((response) => response.data);
};
const getNotes = (url) => {
return axios
.get(url, { headers: { 'PRIVATE-TOKEN': conf.token } })
.then((response) => response.data);
}
const getIssueTimeStats = (id) => {
let url =
conf.host +
`/api/v4/projects/` +
conf.projectId +
`/issues/` +
id +
`/time_stats`;
return axios
.get(url, { headers: { 'PRIVATE-TOKEN': conf.token } })
.then((response) => response.data);
};
const getMergeRequests = (id) => {
let url =
conf.host +
`/api/v4/groups/` +
conf.groupId +
`/merge_requests?search=` +
id +
`&in=title&order_by=updated_at`;
return axios
.get(url, { headers: { 'PRIVATE-TOKEN': conf.token } })
.then((response) => response.data);
};
const processPage = async (page) => {
console.log('Fetching issues at page ' + page);
let issues = await getIssues(page);
for (let i in issues) {
console.log(
'Processing ' + i + ' of ' + issues.length + ' at page ' + page
);
let obj = {
id: issues[i].iid,
created_at: issues[i].created_at,
updated_at: issues[i].updated_at,
closed_at: issues[i].closed_at,
};
console.log('Fetching merge requests of ' + issues[i].iid);
let mergeRequests = await getMergeRequests(issues[i].iid);
if (mergeRequests.length > 0) {
obj.merged_at = mergeRequests[0].merged_at;
obj.merge_request_at = mergeRequests[0].created_at;
}
console.log('Fetching notes of ' + issues[i].iid);
let notes = await getNotes(issues[i]._links.notes);
for (let j in notes) {
if (notes[j].body && notes[j].body.indexOf('assigned to') > -1) {
obj.assigned_at = notes[j].created_at;
}
}
if (obj.assigned_at && obj.merged_at) {
// Collect only the complete ones
closedIssuesWithMR.push(obj);
}
}
};
const analyze = async (items) => {
var items = JSON.parse(fs.readFileSync('data-bak.json', 'utf8'));
// Remove issues those take too long (more than two weeks)
// This maybe a documentation or referenced issue
var filtered = [];
for (let i in items) {
let doing = Math.floor(
Math.abs(
new Date(items[i].merge_request_at) - new Date(items[i].assigned_at)
) /
1000 /
60 /
60
);
let test = Math.floor(
Math.abs(new Date(items[i].closed_at) - new Date(items[i].merged_at)) /
1000 /
60 /
60
);
if (doing < 336 && test < 336) filtered.push(items[i]);
}
items = filtered;
var created_to_closed = [];
var created_to_assigned = [];
var assigned_to_merge_request = [];
var merge_request_to_merged = [];
var merged_to_closed = [];
for (let i in items) {
items[i].created_to_closed = Math.floor(
Math.abs(new Date(items[i].closed_at) - new Date(items[i].created_at)) /
1000 /
60 /
60
);
created_to_closed.push(items[i].created_to_closed);
items[i].created_to_assigned = Math.floor(
Math.abs(new Date(items[i].assigned_at) - new Date(items[i].created_at)) /
1000 /
60 /
60
);
created_to_assigned.push(items[i].created_to_assigned);
items[i].assigned_to_merge_request = Math.floor(
Math.abs(
new Date(items[i].merge_request_at) - new Date(items[i].assigned_at)
) /
1000 /
60 /
60
);
assigned_to_merge_request.push(items[i].assigned_to_merge_request);
items[i].merge_request_to_merged = Math.floor(
Math.abs(
new Date(items[i].merged_at) - new Date(items[i].merge_request_at)
) /
1000 /
60 /
60
);
merge_request_to_merged.push(items[i].merge_request_to_merged);
items[i].merged_to_closed = Math.floor(
Math.abs(new Date(items[i].closed_at) - new Date(items[i].merged_at)) /
1000 /
60 /
60
);
merged_to_closed.push(items[i].merged_to_closed);
}
// Please fast check
// console.log(items[0])
// console.log(items[1])
// console.log(items[2])
// console.log(items[3])
// console.log(items[4])
let reports = {
median: {},
mean: {},
};
// Sort
created_to_closed.sort((a, b) => {
return a - b;
});
created_to_assigned.sort((a, b) => {
return a - b;
});
assigned_to_merge_request.sort((a, b) => {
return a - b;
});
merge_request_to_merged.sort((a, b) => {
return a - b;
});
merged_to_closed.sort((a, b) => {
return a - b;
});
// Median
reports.median.created_to_closed = parseInt(
created_to_closed[Math.floor(created_to_closed.length / 2)]
);
reports.median.created_to_assigned = parseInt(
created_to_assigned[Math.floor(created_to_assigned.length / 2)]
);
reports.median.assigned_to_merge_request = parseInt(
assigned_to_merge_request[Math.floor(assigned_to_merge_request.length / 2)]
);
reports.median.merge_request_to_merged = parseInt(
merge_request_to_merged[Math.floor(merge_request_to_merged.length / 2)]
);
reports.median.merged_to_closed = parseInt(
merged_to_closed[Math.floor(merged_to_closed.length / 2)]
);
// Mean
function add(accumulator, a) {
return accumulator + a;
}
reports.mean.created_to_closed = parseInt(
created_to_closed.reduce(add, 0) / created_to_closed.length
);
reports.mean.created_to_assigned = parseInt(
created_to_assigned.reduce(add, 0) / created_to_assigned.length
);
reports.mean.assigned_to_merge_request = parseInt(
assigned_to_merge_request.reduce(add, 0) / assigned_to_merge_request.length
);
reports.mean.merge_request_to_merged = parseInt(
merge_request_to_merged.reduce(add, 0) / merge_request_to_merged.length
);
reports.mean.merged_to_closed = parseInt(
merged_to_closed.reduce(add, 0) / merged_to_closed.length
);
console.log(reports);
};
const main = async () => {
for (let i = 1; i <= conf.maxPage; i++) {
await processPage(i);
}
await analyze(closedIssuesWithMR);
};
main();
@herpiko
Copy link
Author

herpiko commented Nov 15, 2021

Example output (in hours):

{
  median: {
    created_to_closed: 555,
    created_to_assigned: 5,
    assigned_to_merge_request: 20,
    merge_request_to_merged: 117,
    merged_to_closed: 47
  },
  mean: {
    created_to_closed: 499,
    created_to_assigned: 102,
    assigned_to_merge_request: 141,
    merge_request_to_merged: 177,
    merged_to_closed: 200
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment