@beauraines/toggl-cli 0.10.12 → 0.10.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +1 -1
- package/CHANGELOG.md +7 -0
- package/cmds/ls.mjs +16 -4
- package/cmds/today.mjs +19 -3
- package/cmds/weekly.mjs +19 -3
- package/package.json +3 -2
- package/.env.foo +0 -3
- package/database.js +0 -70
- package/helpers.js +0 -72
- package/toggl.credentials +0 -5
package/.eslintrc.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [0.10.13](https://github.com/beauraines/toggl-cli/compare/v0.10.12...v0.10.13) (2023-04-01)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* Improve table layout ([4652fc3](https://github.com/beauraines/toggl-cli/commit/4652fc3f5e54ca6f0031dfcc035746147b028a5d)), closes [#56](https://github.com/beauraines/toggl-cli/issues/56)
|
|
11
|
+
|
|
5
12
|
### [0.10.12](https://github.com/beauraines/toggl-cli/compare/v0.10.11...v0.10.12) (2023-03-23)
|
|
6
13
|
|
|
7
14
|
### [0.10.11](https://github.com/beauraines/toggl-cli/compare/v0.10.10...v0.10.11) (2023-03-19)
|
package/cmds/ls.mjs
CHANGED
|
@@ -40,11 +40,23 @@ export const handler = async function (argv) {
|
|
|
40
40
|
})
|
|
41
41
|
|
|
42
42
|
const table = new Table({
|
|
43
|
-
head: ['
|
|
43
|
+
head: ['Description', 'Start', 'Stop', 'Duration'],
|
|
44
|
+
chars: { mid: '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' }
|
|
44
45
|
})
|
|
45
|
-
for (
|
|
46
|
-
|
|
46
|
+
for (let i = 0; i < report.length; i++) {
|
|
47
|
+
// First row chars
|
|
48
|
+
const chars = {
|
|
49
|
+
midMid: '┼',
|
|
50
|
+
mid: '─',
|
|
51
|
+
leftMid: '├',
|
|
52
|
+
rightMid: '┤'
|
|
53
|
+
}
|
|
54
|
+
const entry = report[i]
|
|
55
|
+
if (i === 0) {
|
|
56
|
+
table.push([entry.description, entry.start, entry.stop, entry.duration].map((content) => ({ content, chars })))
|
|
57
|
+
} else {
|
|
58
|
+
table.push([entry.description, entry.start, entry.stop, entry.duration])
|
|
59
|
+
}
|
|
47
60
|
}
|
|
48
61
|
console.log(table.toString())
|
|
49
|
-
// console.table(report, ['description', 'start', 'stop', 'duration'])
|
|
50
62
|
}
|
package/cmds/today.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Client from '../client.js'
|
|
2
|
+
import chalk from 'chalk'
|
|
2
3
|
import dayjs from 'dayjs'
|
|
3
4
|
import { getWorkspace, getProjects, formatDuration, formatDurationAsTime } from '../utils.js'
|
|
4
5
|
import dur from 'dayjs/plugin/duration.js'
|
|
@@ -79,10 +80,25 @@ function displayDailyReport (report, format) {
|
|
|
79
80
|
case 'table':
|
|
80
81
|
default:
|
|
81
82
|
const table = new Table({
|
|
82
|
-
head: ['Project', 'Duration']
|
|
83
|
+
head: ['Project', 'Duration'],
|
|
84
|
+
chars: { mid: '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' }
|
|
83
85
|
})
|
|
84
|
-
for (
|
|
85
|
-
|
|
86
|
+
for (let i = 0; i < report.length; i++) {
|
|
87
|
+
// First row chars
|
|
88
|
+
const chars = {
|
|
89
|
+
midMid: '┼',
|
|
90
|
+
mid: '─',
|
|
91
|
+
leftMid: '├',
|
|
92
|
+
rightMid: '┤'
|
|
93
|
+
}
|
|
94
|
+
const project = report[i]
|
|
95
|
+
if (i == 0) {
|
|
96
|
+
table.push([project.project_name, project.duration_formatted].map((content) => ({ content: chalk.grey(content), chars })))
|
|
97
|
+
} else if ( i == report.length - 1) {
|
|
98
|
+
table.push([project.project_name, project.duration_formatted].map((content) => ( {content: chalk.bold(content), chars })))
|
|
99
|
+
} else {
|
|
100
|
+
table.push([chalk.grey(project.project_name), chalk.grey(project.duration_formatted)])
|
|
101
|
+
}
|
|
86
102
|
}
|
|
87
103
|
console.log(table.toString())
|
|
88
104
|
break
|
package/cmds/weekly.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Client from '../client.js'
|
|
2
|
+
import chalk from 'chalk'
|
|
2
3
|
import { getWorkspace, formatDuration, getProjectById } from '../utils.js'
|
|
3
4
|
import dayjs from 'dayjs'
|
|
4
5
|
import dur from 'dayjs/plugin/duration.js'
|
|
@@ -58,10 +59,25 @@ export const handler = async function (argv) {
|
|
|
58
59
|
|
|
59
60
|
const head = Object.keys(reportData[0])
|
|
60
61
|
const table = new Table({
|
|
61
|
-
head
|
|
62
|
+
head,
|
|
63
|
+
chars: { mid: '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' }
|
|
62
64
|
})
|
|
63
|
-
for (
|
|
64
|
-
|
|
65
|
+
for (let i = 0; i < reportData.length; i++) {
|
|
66
|
+
// First row chars
|
|
67
|
+
const chars = {
|
|
68
|
+
midMid: '┼',
|
|
69
|
+
mid: '─',
|
|
70
|
+
leftMid: '├',
|
|
71
|
+
rightMid: '┤'
|
|
72
|
+
}
|
|
73
|
+
const project = reportData[i]
|
|
74
|
+
if (i == 0 ) {
|
|
75
|
+
table.push(Object.values(project).map((content) => ({ content: chalk.grey(content), chars })))
|
|
76
|
+
} else if ( i == reportData.length - 1) {
|
|
77
|
+
table.push(Object.values(project).map((content) => ( {content: chalk.bold(content), chars })))
|
|
78
|
+
} else {
|
|
79
|
+
table.push(Object.values(project).map((content) => ({ content: chalk.grey(content) })))
|
|
80
|
+
}
|
|
65
81
|
}
|
|
66
82
|
console.log(table.toString())
|
|
67
83
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beauraines/toggl-cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.13",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"test": "jest",
|
|
11
|
-
"lint": "eslint . ./**/*.js ./**/*.
|
|
11
|
+
"lint": "eslint . ./**/*.js ./**/*.mjs",
|
|
12
12
|
"lint:fix": "eslint . ./**/*.js ./**/*.mjs --fix",
|
|
13
13
|
"release": "standard-version",
|
|
14
14
|
"should-release": "should-release"
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
|
+
"chalk": "^4.0.0",
|
|
33
34
|
"cli-table3": "^0.6.3",
|
|
34
35
|
"dayjs": "^1.11.6",
|
|
35
36
|
"debug": "^4.3.4",
|
package/.env.foo
DELETED
package/database.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
const homedir = require('os').homedir();
|
|
2
|
-
const sqlite = require('sqlite');
|
|
3
|
-
const sqlite3 = require('sqlite3');
|
|
4
|
-
const {fileExists} = require('./helpers');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Opens the BurnDownStatus SQLite3 Database
|
|
8
|
-
* @param file file name of the SQLite3 DB. If not provided, defaults to ${homedir}/BurnDownStatus.db
|
|
9
|
-
* @returns SQLite database connection
|
|
10
|
-
*/
|
|
11
|
-
async function getDBConnection(file) {
|
|
12
|
-
file = file ? file : `${homedir}/BurnDownStatus.db`;
|
|
13
|
-
if (!await fileExists(file)){
|
|
14
|
-
console.error(`${file} not found`);
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
const db = await sqlite.open({
|
|
18
|
-
filename: `${homedir}/BurnDownStatus.db`,
|
|
19
|
-
driver: sqlite3.Database
|
|
20
|
-
});
|
|
21
|
-
return db;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
26
|
-
* @param {Object} data The burndown data to be inserted into the Burndown database
|
|
27
|
-
*
|
|
28
|
-
* @returns {Object} The result of the database running the provided query
|
|
29
|
-
*/
|
|
30
|
-
async function writeToDb(data) {
|
|
31
|
-
// TODO should take db, query and values as parameters
|
|
32
|
-
let db = await getDBConnection();
|
|
33
|
-
let query = `insert into qa_burndown (date, qa_review_count, qa_validated_count) values (?,?,?) on conflict do update set qa_review_count = excluded.qa_review_count, qa_validated_count = excluded.qa_validated_count`;
|
|
34
|
-
|
|
35
|
-
let values = [data.date, data.qa_review_count, data.qa_validated_count];
|
|
36
|
-
let result = await db.run(query, values);
|
|
37
|
-
await db.close();
|
|
38
|
-
return result;
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
async function insert(database,table,data) {
|
|
44
|
-
let db = await getDBConnection(database);
|
|
45
|
-
|
|
46
|
-
let fields = Object.keys(data[0]).toString();
|
|
47
|
-
let fieldsCount = Object.keys(data[0]).length;
|
|
48
|
-
let valueSubstitution = Array(fieldsCount).fill('?').toString();
|
|
49
|
-
|
|
50
|
-
//TODO figure out how to build upsert
|
|
51
|
-
// on conflict do update set qa_review_count = excluded.qa_review_count, qa_validated_count = excluded.qa_validated_count`;
|
|
52
|
-
let query = `insert into ${table} (${fields}) values (${valueSubstitution});`
|
|
53
|
-
let result = []
|
|
54
|
-
data.forEach(async (row) => {
|
|
55
|
-
// TODO add try/catch for SQL error errno and code
|
|
56
|
-
// TODO switch to db.exec() for more meaningful response
|
|
57
|
-
let response = await db.run(query,Object.values(row));
|
|
58
|
-
result.push(response);
|
|
59
|
-
});
|
|
60
|
-
await db.close();
|
|
61
|
-
// TODO clean up result
|
|
62
|
-
return result;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
module.exports = {
|
|
67
|
-
getDBConnection,
|
|
68
|
-
writeToDb,
|
|
69
|
-
insert
|
|
70
|
-
}
|
package/helpers.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Converts a string to Title Case, using whitespace as the delimiter
|
|
5
|
-
* @param {String} str String to convert
|
|
6
|
-
* @returns The string converted to title case
|
|
7
|
-
*/
|
|
8
|
-
function toTitleCase(str) {
|
|
9
|
-
return str.replace(
|
|
10
|
-
/\w\S*/g,
|
|
11
|
-
function(txt) {
|
|
12
|
-
return txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase();
|
|
13
|
-
}
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Removes newline characters \r and/or \n from a string
|
|
19
|
-
* @param {string} string to remove newlines from
|
|
20
|
-
* @returns string
|
|
21
|
-
*/
|
|
22
|
-
function stripNewLines(string) {
|
|
23
|
-
return string.replace(/\r|\n/g, '');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Checks to see if the specified file exists
|
|
28
|
-
* @param {string} filePath fully qualified path and filename
|
|
29
|
-
* @returns Boolean
|
|
30
|
-
*/
|
|
31
|
-
async function fileExists(filePath) {
|
|
32
|
-
return fs.existsSync(filePath);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Asynchronously reads the entire file contents and returns it.
|
|
37
|
-
* @param {string} filePath fully qualified path and filename
|
|
38
|
-
* @returns any
|
|
39
|
-
*/
|
|
40
|
-
async function readFile(filePath) {
|
|
41
|
-
return fs.readFileSync(filePath,{ encoding: 'utf8', flag: 'r' });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Groups an array by specified properties and sums other specified properties
|
|
46
|
-
*
|
|
47
|
-
* https://stackoverflow.com/questions/46794232/group-objects-by-multiple-properties-in-array-then-sum-up-their-values
|
|
48
|
-
*
|
|
49
|
-
* @param {Array} arr Array of Objects to aggregate
|
|
50
|
-
* @param {Array} groupKeys keys to group by
|
|
51
|
-
* @param {Array} sumKeys keys of properties to sum by
|
|
52
|
-
* @returns Array of Objects
|
|
53
|
-
*/
|
|
54
|
-
function groupAndSum(arr, groupKeys, sumKeys){
|
|
55
|
-
return Object.values(
|
|
56
|
-
arr.reduce((acc,curr)=>{
|
|
57
|
-
const group = groupKeys.map(k => curr[k]).join('-');
|
|
58
|
-
acc[group] = acc[group] || Object.fromEntries(
|
|
59
|
-
groupKeys.map(k => [k, curr[k]]).concat(sumKeys.map(k => [k, 0])));
|
|
60
|
-
sumKeys.forEach(k => acc[group][k] += curr[k]);
|
|
61
|
-
return acc;
|
|
62
|
-
}, {})
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
module.exports = {
|
|
67
|
-
groupAndSum,
|
|
68
|
-
toTitleCase,
|
|
69
|
-
fileExists,
|
|
70
|
-
stripNewLines,
|
|
71
|
-
readFile
|
|
72
|
-
}
|