Skip to content

Commit 5688532

Browse files
committed
Add time tracking
1 parent de0e781 commit 5688532

24 files changed

+608
-45
lines changed

gtt-cancel.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const program = require('commander');
2+
const colors = require('colors');
3+
const moment = require('moment');
4+
5+
const Frame = require('./models/frame');
6+
const Config = require('./include/file-config');
7+
const Cli = require('./include/cli');
8+
const Fs = require('./include/filesystem');
9+
10+
program.parse(process.argv);
11+
12+
let config = new Config(process.cwd());
13+
14+
Fs.find(`"stop": false`, config.frameDir)
15+
.then(frames => {
16+
if (frames.length === 0) Cli.error('No projects started.');
17+
18+
frames.forEach(file => {
19+
let frame = Frame.fromFile(config, file);
20+
Fs.remove(file);
21+
console.log(`Cancel project ${frame.project.magenta} ${frame.resource.type.blue} ${('#' + frame.resource.id).blue}, started ${moment(frame.start).fromNow().green}`);
22+
});
23+
})
24+
.catch(error => Cli.error('Could not read frames.', error));

gtt-config.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const program = require('commander');
2+
3+
const Config = require('./include/file-config');
4+
const Fs = require('./include/filesystem');
5+
6+
let config = new Config(process.cwd());
7+
8+
program
9+
.option('-l, --local', 'edit the local configuration file')
10+
.parse(process.argv);
11+
12+
if (program.local) {
13+
config.assertLocalConfig();
14+
}
15+
16+
Fs.open(program.local ? config.local : config.global);

gtt-delete.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const program = require('commander');
2+
3+
const Frame = require('./models/frame');
4+
const Config = require('./include/file-config');
5+
const Cli = require('./include/cli');
6+
const Fs = require('./include/filesystem');
7+
8+
program
9+
.arguments('[id]')
10+
.parse(process.argv);
11+
12+
let config = new Config(process.cwd());
13+
let id = program.args[0];
14+
15+
if (
16+
(!id || !Fs.exists(Fs.join(config.frameDir, id + '.json')))
17+
&& -Infinity === (id = Fs.newest(config.frameDir))
18+
)
19+
Cli.error('No record found.');
20+
21+
let file = Fs.join(config.frameDir, id.replace('.json', '') + '.json');
22+
let frame = Frame.fromFile(config, file).stopMe();
23+
Fs.remove(file);
24+
console.log(`Deleting record ${frame.id.magenta}`);

gtt-edit.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
const program = require('commander');
22

3-
const open = require('open');
43
const Config = require('./include/file-config');
5-
6-
let config = new Config(__dirname);
4+
const Cli = require('./include/cli');
5+
const Fs = require('./include/filesystem');
76

87
program
9-
.option('-l, --local', 'edit the local configuration file')
8+
.arguments('[id]')
109
.parse(process.argv);
1110

12-
if (program.local) {
13-
config.assertLocalConfig();
14-
}
11+
let config = new Config(process.cwd());
12+
let id = program.args[0];
13+
14+
if (
15+
(!id || !Fs.exists(Fs.join(config.frameDir, id + '.json')))
16+
&& -Infinity === (id = Fs.newest(config.frameDir))
17+
)
18+
Cli.error('No record found.');
1519

16-
open(program.local ? config.local : config.global);
20+
Fs.open(Fs.join(config.frameDir, id.replace('.json', '') + '.json'));

gtt-log.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const _ = require('underscore');
2+
const program = require('commander');
3+
const colors = require('colors');
4+
const moment = require('moment');
5+
6+
const Frame = require('./models/frame');
7+
const Config = require('./include/file-config');
8+
const Time = require('./models/time');
9+
const Fs = require('./include/filesystem');
10+
11+
program.parse(process.argv);
12+
13+
let config = new Config(__dirname);
14+
15+
let frames = {};
16+
let times = {};
17+
18+
function toHumanReadable(input) {
19+
return Time.toHumanReadable(Math.ceil(input), config.get('hoursPerDay'), config.get('timeFormat'));
20+
}
21+
22+
Fs.readDir(config.frameDir).forEach(file => {
23+
let frame = Frame.fromFile(config, Fs.join(config.frameDir, file));
24+
if (frame.stop === false) return;
25+
let date = moment(frame.start).format('YYYY-MM-DD');
26+
27+
if (!frames[date]) frames[date] = [];
28+
if (!times[date]) times[date] = 0;
29+
30+
frames[date].push(frame);
31+
times[date] += Math.ceil(frame.duration);
32+
});
33+
34+
_.each(frames, (frames, date) => {
35+
console.log(`${moment(date).format('MMMM Do YYYY')} (${toHumanReadable(times[date])})`.green);
36+
frames.forEach(frame => {
37+
console.log(` ${frame.id} ${moment(frame.start).format('hh:mm').green} to ${moment(frame.stop).format('hh:mm').green}\t${toHumanReadable(frame.duration)}\t${frame.project.magenta}\t${(frame.resource.type + ' #' + frame.resource.id).blue}`)
38+
});
39+
});

gtt-report.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,10 @@ new Promise(resolve => {
176176

177177
// make report
178178
.then(() => new Promise(resolve => {
179-
if (report.issues.length === 0 && report.mergeRequests.length === 0) Cli.error('No issues or merge requests matched your criteria.');
180-
Cli.list(`${Cli.output} Making report`);
179+
if (report.issues.length === 0 && report.mergeRequests.length === 0)
180+
Cli.error('No issues or merge requests matched your criteria.');
181181

182+
Cli.list(`${Cli.output} Making report`);
182183
output = new Output[config.get('output')](config, report);
183184
output.make();
184185
Cli.mark();

gtt-start.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const colors = require('colors');
2+
const moment = require('moment');
3+
const program = require('commander');
4+
5+
const Frame = require('./models/frame');
6+
const Config = require('./include/file-config');
7+
const Cli = require('./include/cli');
8+
const Fs = require('./include/filesystem');
9+
10+
program
11+
.arguments('[project] [id]')
12+
.option('-t, --type <type>', 'specify resource type: issue, merge_request')
13+
.parse(process.argv);
14+
15+
let config = new Config(process.cwd());
16+
17+
if (program.args.length < 2 && !config.get('project')) Cli.error('No project set');
18+
if (program.args.length === 2) config.set('project', program.args[0]);
19+
20+
let id = program.args.length === 1 ? parseInt(program.args[0]) : parseInt(program.args[1]);
21+
if (!id) Cli.error('Wrong or missing issue/merge_request id');
22+
23+
let type = program.type ? program.type : 'issue';
24+
25+
Fs.find(`"stop": false`, config.frameDir)
26+
.then(frames => {
27+
if (frames.length > 0) Cli.error("Already running. Please stop it first with 'gtt stop'.");
28+
29+
new Frame(config, id, type).startMe();
30+
console.log(`Starting project ${config.get('project').magenta} ${type.blue} ${('#' + id).blue} at ${moment().format('HH:mm').green}`);
31+
})
32+
.catch(error => Cli.error('Could not write frame.', error));

gtt-stop.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const program = require('commander');
2+
const colors = require('colors');
3+
const moment = require('moment');
4+
5+
const Frame = require('./models/frame');
6+
const Config = require('./include/file-config');
7+
const Cli = require('./include/cli');
8+
const Fs = require('./include/filesystem');
9+
10+
program.parse(process.argv);
11+
12+
let config = new Config(__dirname);
13+
14+
Fs.find(`"stop": false`, config.frameDir)
15+
.then(frames => {
16+
if (frames.length === 0) Cli.error('No projects started.');
17+
18+
frames.forEach(file => {
19+
let frame = Frame.fromFile(config, file).stopMe();
20+
console.log(`Stopping project ${frame.project.magenta} ${frame.resource.type.blue} ${('#' + frame.resource.id).blue}, started ${moment(frame.start).fromNow().green} (id: ${frame.id})`);
21+
});
22+
})
23+
.catch(error => Cli.error('Could not read frames.', error));

gtt-sync.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
const _ = require('underscore');
2+
const moment = require('moment');
3+
const program = require('commander');
4+
5+
const FrameCollection = require('./models/frameCollection');
6+
const Issue = require('./models/issue');
7+
const MergeRequest = require('./models/mergeRequest');
8+
const Config = require('./include/file-config');
9+
const Cli = require('./include/cli');
10+
11+
program.parse(process.argv);
12+
13+
let config = new Config(process.cwd());
14+
let frames = new FrameCollection(config);
15+
16+
let classes = {
17+
issue: Issue,
18+
merge_request: MergeRequest
19+
};
20+
let resources = {
21+
issue: {},
22+
merge_request: {}
23+
};
24+
25+
function createTime(frame, time) {
26+
return new Promise((resolve, reject) => {
27+
resources[frame.resource.type][frame.resource.id].createTime(Math.ceil(time))
28+
.then(() => resources[frame.resource.type][frame.resource.id].getNotes())
29+
.then(() => {
30+
frame.notes.push({
31+
id: resources[frame.resource.type][frame.resource.id].notes[0].id,
32+
time: Math.ceil(time)
33+
});
34+
frame.write();
35+
resolve();
36+
})
37+
.catch(error => reject(error))
38+
});
39+
}
40+
41+
Cli.bar(`${Cli.process} Syncing time records...`, frames.length);
42+
43+
frames.forEach((frame, done) => {
44+
// filter out frames, that don't need an update
45+
if (Math.ceil(frame.duration) === _.reduce(frame.notes, (n, m) => (n + m.time), 0)) {
46+
Cli.advance();
47+
return done();
48+
}
49+
50+
new Promise(resolve => resolve())
51+
52+
// set resource if it isn't already set
53+
.then(() => new Promise((resolve, reject) => {
54+
if (resources[frame.resource.type][frame.resource.id]) return resolve();
55+
resources[frame.resource.type][frame.resource.id] = new classes[frame.resource.type](config, {});
56+
resources[frame.resource.type][frame.resource.id].make(frame.project, frame.resource.id)
57+
.then(() => resolve())
58+
.catch(error => reject(error));
59+
}))
60+
.catch(error => Cli.x(`Could not resolve issue/merge_request ${frame.resource.id} on "${frame.project}"`, error))
61+
62+
// set notes if not already set
63+
.then(() => new Promise((resolve, reject) => {
64+
let notes;
65+
if ((notes = resources[frame.resource.type][frame.resource.id].notes) && notes.length > 0) return;
66+
resources[frame.resource.type][frame.resource.id].getNotes()
67+
.then(() => resolve())
68+
.catch(error => reject(error));
69+
}))
70+
.catch(error => Cli.x(`Could not get notes from "${frame.project}"`, error))
71+
72+
// create note if completely missing
73+
.then(() => new Promise((resolve, reject) => {
74+
if (frame.notes.length > 0) return resolve();
75+
createTime(frame, frame.duration)
76+
.then(() => {
77+
Cli.advance();
78+
done();
79+
})
80+
.catch(error => reject(error))
81+
}))
82+
.catch(error => Cli.error('Could not create time spent.', error))
83+
84+
// check for mismatches and update times
85+
.then(() => new Promise((resolve, reject) => {
86+
let diff = Math.ceil(frame.duration) - parseInt(_.reduce(frame.notes, (n, m) => (n + m.time), 0));
87+
createTime(frame, diff)
88+
.then(() => {
89+
Cli.advance();
90+
done();
91+
})
92+
.catch(error => reject(error))
93+
}))
94+
.catch(error => Cli.error('Could not create time spent.', error))
95+
});

gtt.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
#!/usr/bin/env node
22

3-
const version = '1.0.3';
3+
const version = '1.1.0';
44
const program = require('commander');
55

66
program
77
.version(version)
8-
.command('edit', 'edit the configuration file in your default editor')
8+
.command('start [project] [id]', 'start monitoring time for the given project and resource id')
9+
.command('stop', 'stop monitoring time')
10+
.command('cancel', 'cancel and discard active monitoring time')
11+
.command('log', 'log recorded time records')
12+
.command('sync', 'sync local time records to GitLab')
13+
.command('edit [id]', 'edit time record by the given id')
14+
.command('delete [id]', 'delete time record by the given id')
915
.command('report [project] [ids]', 'generate a report for the given project and issues')
10-
.parse(process.argv);
16+
.command('config', 'edit the configuration file in your default editor')
17+
.parse(process.argv);

0 commit comments

Comments
 (0)