Skip to content

Instantly share code, notes, and snippets.

@bhouston
Created August 8, 2020 16:14
Show Gist options
  • Save bhouston/f83f099fe2996a84e9584ee90534dfdb to your computer and use it in GitHub Desktop.
Save bhouston/f83f099fe2996a84e9584ee90534dfdb to your computer and use it in GitHub Desktop.
parse gitlab time spends from notes
import { Spend } from '../plan/model.mjs';
import nodemon from 'nodemon';
import { log, logPush, logPop } from '../logging.mjs';
var spendBodyRegex = /(?<operation>(added|subtracted)) (?<duration>([0-9]+[mowdhs]+ )*)(of time spent at )(?<date>[0-9\-]+)/;
var spendResetBody = "removed time spent";
//var durationRegex = new RegExp( '^((?<months>[0-9]+)mo(\\w)*)?((?<weeks>[0-9]+)w(\\w)*)?((?<days>[0-9]+)d(\\w)*)?((?<hours>[0-9]+)h(\\w)*)?((?<minutes>[0-9]+)m(\\w)*)?((?<seconds>[0-9]+)s(\\w)*)?' );
const durationRegex = /((?<months>[0-9]+)mo(\w)*)?((?<weeks>[0-9]+)w(\w)*)?((?<days>[0-9]+)d(\w)*)?((?<hours>[0-9]+)h(\w)*)?((?<minutes>[0-9]+)m(\w)*)?((?<seconds>[0-9]+)s(\w)*)?/gm;
//var durationRegex = /((((?<mo>[0-9]+)mo)|((?<w>[0-9]+)w)|((?<d>[0-9]+)d)|((?<h>[0-9]+)h)|((?<m>[0-9]+)m)|((?<s>[0-9]+)s))( )*)+/g;
function parseIntIfExists( result ) {
if( result !== undefined ) {
return parseInt( result );
}
return 0;
}
function parseDurationToSeconds( duration ) {
var totalDurationInSeconds = 0;
var groups = {};
let result;
while ( result = durationRegex.exec(duration) ) {
//console.log( 'result.groups', result.groups );
// This is necessary to avoid infinite loops with zero-width matches
if (result.index === durationRegex.lastIndex) {
durationRegex.lastIndex++;
}
//console.log( result );
// roll up based on custom GitLab unit usage:
// https://docs.gitlab.com/ee/user/project/time_tracking.html#configuration
var durationInMonths = parseIntIfExists( result.groups.months );
var durationInWeeks = parseIntIfExists( result.groups.weeks ) + 4 * durationInMonths;
var durationInDays = parseIntIfExists( result.groups.days ) + 5 * durationInWeeks;
var durationInHours = parseIntIfExists( result.groups.hours ) + 8 * durationInDays;
var durationInMinutes = parseIntIfExists( result.groups.minutes ) + 60 * durationInHours;
var durationInSeconds = parseIntIfExists( result.groups.seconds ) + 60 * durationInMinutes;
totalDurationInSeconds += durationInSeconds;
//console.log( 'totalDurationInSeconds', totalDurationInSeconds );
}
return totalDurationInSeconds;
}
function isSpendAutomatic( username, spendDate ) {
if( username === 'threekit_auth' ) return true;
if( username === 'bhouston' && spendDate.getTime() < new Date( 2020, 0, 1 )) return true;
return false;
}
function processNoteSpends( parent, notes, newSpendCallback ) {
// TODO: support reset of spends.
var reversedNotes = [];
if( notes ) {
notes.forEach( note => {
reversedNotes.unshift( note );
});
}
var totalDuration = 0;
reversedNotes.forEach( note => {
if( note.system ) {
var result = spendBodyRegex.exec( note.body );
if( result ) {
var scale = ( result.groups.operation === 'added' ) ? 1 : -1;
var duration = parseDurationToSeconds( result.groups.duration ) * scale;
var date = new Date( result.groups.date );
if( ! isSpendAutomatic( note.author.username, date ) ) {
newSpendCallback( note.author.username, date, duration, 'system' );
}
totalDuration += duration;
}
else if( note.body === spendResetBody ) {
var duration = ( - totalDuration );
var date = new Date( note.created_at );
if( isSpendAutomatic( note.author.username, date ) ) {
newSpendCallback( note.author.username, date, duration, 'system' );
}
totalDuration += duration;
}
}
else if( note.internal ) {
var duration = note.duration;
var date = new Date( note.created_at );
if( isSpendAutomatic( note.username, date ) ){
newSpendCallback( note.username, date, note.duration, 'internal' );
}
totalDuration += duration;
}
});
// if there is left over time, it was because the spend was done in the description of the issue.
if( parent.original_time_stats.total_time_spent > totalDuration ) {
if( parent.labels['type'] !== 'epic' ) { // this can not be used on an epic, because we set the time on it.
var duration = parent.original_time_stats.total_time_spent - totalDuration;
newSpendCallback( parent.author.username, new Date( parent.created_at ), duration, 'remainder' );
}
}
}
export function recordNoteSpends( plan, task, parent, notes ) {
return processNoteSpends( parent, notes, ( username, date, duration, spendType ) => {
let spend = plan.requestSpend( username, date, task, duration );
if( (!spend.existing) && parent && parent.iid === 2598 ) {
spend.existing = true;
log( `processNoteSpends( epic ${parent.iid} task ${task.iid} date ${date} duration ${duration} type ${spendType} )` );
log( JSON.stringify( notes ) );
}
});
}
export function remapNoteSpends( parent, notes, issues ) {
return processNoteSpends( parent, notes, ( username, date, duration ) => {
issues.forEach( issue => {
issue.notes.push( {
'internal': true,
'username': username,
'created_at': date.toISOString(),
'duration': Math.round( duration / issues.length )
});
})
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment