Skip to content

Commit fea8250

Browse files
committed
add csv and file output
1 parent aa264a6 commit fea8250

File tree

11 files changed

+396
-41
lines changed

11 files changed

+396
-41
lines changed

gtt-report.js

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
const _ = require('underscore');
2+
const fs = require('fs');
23
const program = require('commander');
34

45
const Cli = require('./include/cli');
56
const Config = require('./include/file-config');
67
const Report = require('./models/report');
78

89
const Output = {
9-
table: require('./output/table')
10+
table: require('./output/table'),
11+
csv: require('./output/csv')
1012
};
1113

1214
// this collects options
@@ -28,7 +30,7 @@ program
2830
.option('-q --query <query>', 'query the given data types: issues, merge_requests', collect, null)
2931
.option('-r --report <report>', 'include in the report: stats, issues, merge_requests, records', collect, null)
3032
.option('-o --output <output>', 'use the given output', collect, null)
31-
.option('-l --file <file>', 'save report to the given file', collect, null)
33+
.option('-l --file <file>', 'save report to the given file')
3234
.option('--include_by_labels <labels>', 'only include issues that have the given labels', collect, null)
3335
.option('--exclude_by_labels <labels>', 'exclude issues that have the given labels', collect, null)
3436
.option('--include_labels <labels>', 'only include the given labels in the report', collect, null)
@@ -41,6 +43,7 @@ program
4143
.option('--issue_columns <columns>', 'include the given columns in the issue part of the report', collect, null)
4244
.option('--merge_request_columns <columns>', 'include the given columns in the merge request part of the report', collect, null)
4345
.option('--user_columns', 'include user columns in the report')
46+
.option('--quiet', 'only output report')
4447
.parse(process.argv);
4548

4649
// init helpers
@@ -71,8 +74,11 @@ config
7174
.set('mergeRequestColumns', program.merge_request_columns)
7275
.set('noHeadlines', program.no_headlines)
7376
.set('noWarnings', program.no_warnings)
77+
.set('quiet', program.quiet)
7478
.set('userColumns', program.user_columns);
7579

80+
Cli.quiet = config.get('quiet');
81+
7682
// warnings
7783
if (config.get('iids').length > 1 && config.get('query').length > 1) {
7884
Cli.warn(`The ids argument is ignored when querying multiple data types`);
@@ -87,11 +93,27 @@ if (!Output[config.get('output')]) {
8793
Cli.error(`The output ${config.get('output')} doesn't exist`);
8894
}
8995

96+
// create report
9097
let report = new Report(config), output;
9198

92-
Cli.list(`${Cli.look} Resolving project "${config.get('project')}"`);
93-
report
94-
.project()
99+
// file prompt
100+
new Promise(resolve => {
101+
if (config.get('file') && fs.existsSync(config.get('file'))) {
102+
Cli.ask(`The file "${config.get('file')}" already exists. Overwrite?`)
103+
.then(() => resolve())
104+
.catch(error => Cli.error(`can't write file.`, error));
105+
} else {
106+
resolve();
107+
}
108+
})
109+
110+
// get project
111+
.then(() => {
112+
Cli.list(`${Cli.look} Resolving project "${config.get('project')}"`);
113+
return report.project();
114+
})
115+
116+
// get members
95117
.then(() => new Promise((resolve, reject) => {
96118
if (!config.get('userColumns')) return resolve();
97119

@@ -165,7 +187,11 @@ report
165187

166188
// print report
167189
.then(() => new Promise(resolve => {
168-
output.toStdOut();
190+
if (config.get('file')) {
191+
output.toFile(config.get('file'));
192+
} else {
193+
output.toStdOut();
194+
}
169195
resolve();
170196
}))
171197
.catch(error => Cli.x(`could not print report.`, error))

include/cli.js

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const _ = require('underscore');
22
const colors = require('colors');
3+
const prompt = require('prompt');
34
const spinner = require('node-spinner')();
45
const cursor = require('cli-cursor');
56
const progress = require('progress');
@@ -38,19 +39,53 @@ class cli {
3839
// return '🍺';
3940
}
4041

42+
/**
43+
* ask
44+
* @param message
45+
* @returns {Promise}
46+
*/
47+
static ask(message) {
48+
return new Promise((resolve, reject) => {
49+
prompt.start();
50+
51+
let question = {
52+
name: 'yesno',
53+
message: message,
54+
validator: /y[es]*|n[o]?/,
55+
warning: 'Must respond yes or no',
56+
default: 'yes'
57+
};
58+
59+
prompt.get(question, function (error, result) {
60+
if (error || result.yesno === 'no' || result.yesno === 'n') return reject(error);
61+
62+
resolve();
63+
});
64+
});
65+
}
66+
67+
/**
68+
* print
69+
* @param string
70+
*/
71+
static out(string) {
72+
if (cli.quiet) return;
73+
process.stdout.write(string);
74+
}
75+
4176
/**
4277
* print done message
4378
*/
4479
static done() {
45-
console.log(`\n${cli.party} Finished!`.green);
80+
cli.out(`\n${cli.party} Finished!\n`.green);
4681
}
4782

4883
/**
4984
* print a warning
5085
* @param message
5186
*/
5287
static warn(message) {
53-
console.log(` Warning: ${message} `.bgWhite.black);
88+
cli.out(` Warning: ${message} `.bgWhite.black);
5489
}
5590

5691
/**
@@ -62,6 +97,8 @@ class cli {
6297
static bar(message, total) {
6398
cli.resolve(false);
6499

100+
if (cli.quiet) return cli.promise();
101+
65102
this.active = {
66103
started: new Date(),
67104
message: `\r${message}... `.bold.grey,
@@ -72,8 +109,8 @@ class cli {
72109
renderThrottle: 100
73110
}),
74111
interval: setInterval(() => {
75-
if (!this.active.bar || this.active.bar.complete) return clearInterval(this.active.interval);
76-
this.tick(0);
112+
if (!cli.active.bar || cli.active.bar.complete) return clearInterval(cli.active.interval);
113+
cli.tick(0);
77114
}, 1000)
78115
};
79116

@@ -86,19 +123,19 @@ class cli {
86123
* @param amount
87124
*/
88125
static tick(amount = 0) {
89-
if (!this.active.bar || !this.active.started) return;
126+
if (!cli.active.bar || !cli.active.started) return;
90127

91128
let left;
92129

93-
if(this.active.bar.curr > 0) {
94-
let elapsed = Math.ceil((new Date() - this.active.started) / 1000);
95-
left = ((elapsed / this.active.bar.curr) * (this.active.bar.total - this.active.bar.curr)) / 60;
130+
if (cli.active.bar.curr > 0) {
131+
let elapsed = Math.ceil((new Date() - cli.active.started) / 1000);
132+
left = ((elapsed / cli.active.bar.curr) * (cli.active.bar.total - cli.active.bar.curr)) / 60;
96133
left = left < 1 ? `<1` : Math.ceil(left);
97134
} else {
98135
left = 0;
99136
}
100137

101-
this.active.bar.tick(amount, {
138+
cli.active.bar.tick(amount, {
102139
minutes: left
103140
});
104141
}
@@ -107,7 +144,7 @@ class cli {
107144
* advance an existing bar
108145
*/
109146
static advance() {
110-
this.tick(1);
147+
cli.tick(1);
111148
}
112149

113150
/**
@@ -120,7 +157,7 @@ class cli {
120157

121158
this.active = {message: `\r${message}... `.bold.grey};
122159
this.active.interval = setInterval(() => {
123-
process.stdout.write(this.active.message + spinner.next().bold.blue);
160+
cli.out(cli.active.message + spinner.next().bold.blue);
124161
}, 100);
125162

126163
return cli.promise();
@@ -132,7 +169,7 @@ class cli {
132169
*/
133170
static mark() {
134171
cli.resolve();
135-
process.stdout.write(`${this.active.message}` + `✓\n`.green);
172+
cli.out(`${cli.active.message}` + `✓\n`.green);
136173

137174
return cli.promise();
138175
}
@@ -145,7 +182,7 @@ class cli {
145182
*/
146183
static x(message = false, error = false) {
147184
cli.resolve();
148-
process.stdout.write(`${this.active.message}` + `✗\n`.red);
185+
cli.out(`${cli.active.message}` + `✗\n`.red);
149186

150187
if (message) cli.error(message, error);
151188
return cli.promise();
@@ -156,7 +193,7 @@ class cli {
156193
*/
157194
static resolve(show = true) {
158195
cursor.toggle(show);
159-
if (this.active && this.active.interval) clearInterval(this.active.interval);
196+
if (cli.active && cli.active.interval) clearInterval(cli.active.interval);
160197
}
161198

162199
/**
@@ -168,7 +205,7 @@ class cli {
168205
static error(message, error) {
169206
cli.resolve();
170207

171-
console.log(` Error: ${message} `.bgRed.white);
208+
cli.out(` Error: ${message} `.bgRed.white + '\n');
172209
if (error) console.log(error);
173210

174211
process.exit(1);

include/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const defaults = {
2828
report: ['stats', 'issues', 'merge_requests', 'records'],
2929
noHeadlines: false,
3030
noWarnings: false,
31+
quiet: false,
3132
_perPage: 100,
3233
_parallel: 10
3334
};

models/issue.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const _ = require('underscore');
12
const moment = require('moment');
23

34
const hasTimes = require('./hasTimes');
@@ -35,6 +36,9 @@ class issue extends hasTimes {
3536
}
3637

3738
get labels() {
39+
let labels = _.difference(this.data.labels, this.config.get('excludeLabels'));
40+
let include = this.config.get('includeLabels');
41+
return include.length > 0 ? _.intersection(labels, include) : labels;
3842
}
3943

4044
get milestone() {

models/mergeRequest.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ class mergeRequest extends hasTimes {
99
this.data = data;
1010
}
1111

12+
/*
13+
* properties
14+
*/
1215
get iid() {
1316
return this.data.iid;
1417
}
@@ -17,6 +20,60 @@ class mergeRequest extends hasTimes {
1720
return this.data.id;
1821
}
1922

23+
get title() {
24+
return this.data.title;
25+
}
26+
27+
get project_id() {
28+
return this.data.project_id;
29+
}
30+
31+
get description() {
32+
return this.data.description;
33+
}
34+
35+
get labels() {
36+
let labels = _.difference(this.data.labels, this.config.get('excludeLabels'));
37+
let include = this.config.get('includeLabels');
38+
return include.length > 0 ? _.intersection(labels, include) : labels;
39+
}
40+
41+
get milestone() {
42+
return this.data.milestone ? this.data.milestone.title : null;
43+
}
44+
45+
get assignee() {
46+
return this.data.assignee ? this.data.assignee.username : null;
47+
}
48+
49+
get author() {
50+
return this.data.author.username;
51+
}
52+
53+
get updated_at() {
54+
return moment(this.data.updated_at);
55+
}
56+
57+
get created_at() {
58+
return moment(this.data.created_at);
59+
}
60+
61+
get state() {
62+
return this.data.state;
63+
}
64+
65+
get spent() {
66+
return this.config.toHumanReadable(this.timeSpent);
67+
}
68+
69+
get total_spent() {
70+
return this.stats ? this.config.toHumanReadable(this.stats.total_time_spent) : null;
71+
}
72+
73+
get total_estimate() {
74+
return this.stats ? this.config.toHumanReadable(this.stats.time_estimate) : null;
75+
}
76+
2077
get _type() {
2178
return 'merge_requests';
2279
}

output/base.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
const _ = require('underscore');
2+
const fs = require('fs');
23
const moment = require('moment');
34

45
const defaultFormats = {
5-
headline: h => `\n${h}\n\n`,
6-
warning: w => w
6+
headline: h => `${h}\n`,
7+
warning: w => w,
8+
write: w => `\n${w}`
79
};
810

911
/**
@@ -51,7 +53,7 @@ class base {
5153
* @returns {base}
5254
*/
5355
write(string) {
54-
this.out += string;
56+
this.out += this.formats.write(string);
5557
return this;
5658
}
5759

@@ -86,8 +88,8 @@ class base {
8688
/**
8789
* render to file
8890
*/
89-
toFile() {
90-
91+
toFile(file) {
92+
fs.writeFileSync(file, this.out);
9193
}
9294

9395
/**

0 commit comments

Comments
 (0)