@beauraines/toggl-cli 0.10.5
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 +16 -0
- package/.github/dependabot.yml +11 -0
- package/.github/workflows/main.yml +27 -0
- package/.github/workflows/publish.yml +54 -0
- package/CHANGELOG.md +38 -0
- package/README.md +85 -0
- package/cli.js +13 -0
- package/client.js +29 -0
- package/cmds/continue.mjs +55 -0
- package/cmds/currentTimeEntry.mjs +16 -0
- package/cmds/edit.mjs +84 -0
- package/cmds/index.mjs +26 -0
- package/cmds/ls.mjs +30 -0
- package/cmds/ls.test.js +6 -0
- package/cmds/me.mjs +44 -0
- package/cmds/projects/add.mjs +7 -0
- package/cmds/projects/index.mjs +3 -0
- package/cmds/projects/list.mjs +25 -0
- package/cmds/projects.mjs +11 -0
- package/cmds/startTimeEntry.mjs +38 -0
- package/cmds/stopTimeEntry.mjs +18 -0
- package/cmds/today.mjs +83 -0
- package/cmds/web.mjs +23 -0
- package/cmds/weekly.mjs +98 -0
- package/cmds/workspace.mjs +10 -0
- package/cmds/workspaces/add.mjs +7 -0
- package/cmds/workspaces/index.mjs +3 -0
- package/cmds/workspaces/list.mjs +14 -0
- package/package.json +50 -0
- package/standalone/currentTimeEntry.js +12 -0
- package/standalone/getWorkspaces.js +11 -0
- package/standalone/listProjects.js +21 -0
- package/standalone/startTimeEntry.js +47 -0
- package/standalone/stopCurrentTimeEntry.js +21 -0
- package/standalone/todayReport.js +101 -0
- package/standalone/weeklyReport.js +57 -0
- package/utils.js +133 -0
package/cmds/today.mjs
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import Client from '../client.js'
|
|
2
|
+
import dayjs from 'dayjs'
|
|
3
|
+
import {getWorkspace,getProjects, formatDuration,formatDurationAsTime} from '../utils.js'
|
|
4
|
+
import dur from 'dayjs/plugin/duration.js'
|
|
5
|
+
import relativeTime from 'dayjs/plugin/relativeTime.js'
|
|
6
|
+
dayjs.extend(relativeTime)
|
|
7
|
+
dayjs.extend(dur)
|
|
8
|
+
|
|
9
|
+
export const command = 'today'
|
|
10
|
+
export const desc = 'Reports today\'s activities by project'
|
|
11
|
+
export const builder = {}
|
|
12
|
+
|
|
13
|
+
export const handler = async function (argv) {
|
|
14
|
+
const client = Client()
|
|
15
|
+
const workspace = await getWorkspace()
|
|
16
|
+
const projects = await getProjects(workspace.id)
|
|
17
|
+
const params = {
|
|
18
|
+
start_date: dayjs().startOf('day').toISOString(),
|
|
19
|
+
end_date: dayjs().toISOString()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const timeEntries = await client.timeEntries.list(params)
|
|
23
|
+
|
|
24
|
+
let todaysProjects = []
|
|
25
|
+
timeEntries.map(e => {
|
|
26
|
+
todaysProjects.push(e.pid)
|
|
27
|
+
})
|
|
28
|
+
todaysProjects = [...new Set(todaysProjects)]
|
|
29
|
+
|
|
30
|
+
const report = []
|
|
31
|
+
todaysProjects.map(p => {
|
|
32
|
+
let total = 0
|
|
33
|
+
timeEntries.filter(x => x.pid == p).map(e => {
|
|
34
|
+
if (e.duration >= 0) {
|
|
35
|
+
total += e.duration // UNLESS e.duration is less than zero - meaning currently running
|
|
36
|
+
} else {
|
|
37
|
+
const startTime = dayjs.unix(e.duration * -1)
|
|
38
|
+
const duration = dayjs().diff(startTime, 's')
|
|
39
|
+
total += duration
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
const project = projects.find(x => x.id == p)
|
|
43
|
+
report.push({
|
|
44
|
+
project,
|
|
45
|
+
project_name: project?.name,
|
|
46
|
+
seconds: total,
|
|
47
|
+
duration_formatted: formatDuration(total * 1000),
|
|
48
|
+
duration: formatDurationAsTime(total * 1000)
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// Compute total row
|
|
53
|
+
const totalRow = {
|
|
54
|
+
project_name: 'Total'
|
|
55
|
+
}
|
|
56
|
+
totalRow.seconds = report.reduce((total, project) => {
|
|
57
|
+
return total + project.seconds
|
|
58
|
+
}, 0)
|
|
59
|
+
totalRow.duration_formatted = formatDuration(totalRow.seconds * 1000)
|
|
60
|
+
totalRow.duration = formatDurationAsTime(totalRow.seconds * 1000)
|
|
61
|
+
report.push(totalRow)
|
|
62
|
+
|
|
63
|
+
// TODO make format a CLI option
|
|
64
|
+
const format = 'table' // csv | json | table defaults to table
|
|
65
|
+
displayDailyReport(report, format)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// TODO should this be moved to a formatter file?
|
|
69
|
+
function displayDailyReport (report, format) {
|
|
70
|
+
switch (format) {
|
|
71
|
+
case 'csv':
|
|
72
|
+
// TODO maybe use jsontocsv
|
|
73
|
+
console.log(JSON.stringify(report))
|
|
74
|
+
break
|
|
75
|
+
case 'json':
|
|
76
|
+
console.log(JSON.stringify(report))
|
|
77
|
+
break
|
|
78
|
+
case 'table':
|
|
79
|
+
default:
|
|
80
|
+
console.table(report, ['project_name', 'duration_formatted'])
|
|
81
|
+
break
|
|
82
|
+
}
|
|
83
|
+
}
|
package/cmds/web.mjs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import open from 'open'
|
|
2
|
+
|
|
3
|
+
export const command = 'web'
|
|
4
|
+
export const desc = 'Opens toggl track website with default browser, defaults to the timer page'
|
|
5
|
+
export const builder = {
|
|
6
|
+
t: { alias: ['timer'], describe: 'opens the timer page ', requiresArg: false },
|
|
7
|
+
r: { alias: ['reports'], describe: 'opens the reports page ', requiresArg: false },
|
|
8
|
+
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const handler = async function (argv) {
|
|
12
|
+
let baseUrl = 'https://track.toggl.com'
|
|
13
|
+
let pageUrl
|
|
14
|
+
if ( argv.timer) {
|
|
15
|
+
pageUrl = 'timer'
|
|
16
|
+
} else if ( argv.reports) {
|
|
17
|
+
pageUrl = 'reports'
|
|
18
|
+
} else {
|
|
19
|
+
pageUrl = 'timer'
|
|
20
|
+
}
|
|
21
|
+
console.log(`Opening ${baseUrl}/${pageUrl}`)
|
|
22
|
+
open(`${baseUrl}/${pageUrl}`)
|
|
23
|
+
}
|
package/cmds/weekly.mjs
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import Client from '../client.js'
|
|
2
|
+
import { getWorkspace, formatDuration, getProjectById } from '../utils.js'
|
|
3
|
+
import dayjs from 'dayjs'
|
|
4
|
+
import dur from 'dayjs/plugin/duration.js'
|
|
5
|
+
import relativeTime from 'dayjs/plugin/relativeTime.js'
|
|
6
|
+
dayjs.extend(relativeTime)
|
|
7
|
+
dayjs.extend(dur)
|
|
8
|
+
|
|
9
|
+
export const command = 'week'
|
|
10
|
+
// FIXME descriptions
|
|
11
|
+
export const desc = 'Weekly project summary by day'
|
|
12
|
+
export const builder = {}
|
|
13
|
+
|
|
14
|
+
export const handler = async function (argv) {
|
|
15
|
+
const client = Client()
|
|
16
|
+
const workspace = await getWorkspace()
|
|
17
|
+
|
|
18
|
+
const params = { } // Leave this for future options, like rounding
|
|
19
|
+
const weeklyReport = await client.reports.weekly(workspace.id, params)
|
|
20
|
+
|
|
21
|
+
const reportData = []
|
|
22
|
+
const totals = [0, 0, 0, 0, 0, 0, 0] // ? Is there a better way to do this?
|
|
23
|
+
for (const project of weeklyReport) {
|
|
24
|
+
const currentProject = await getProjectById(workspace.id, project.project_id)
|
|
25
|
+
const row = {
|
|
26
|
+
projectName: currentProject.name,
|
|
27
|
+
Total: 0
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < project.seconds.length; i++) {
|
|
31
|
+
const element = project.seconds[i]
|
|
32
|
+
const date = dayjs().startOf('week').add(i, 'days').format('ddd MM-DD')
|
|
33
|
+
const duration = element || 0
|
|
34
|
+
row[date] = formatDuration(duration * 1000)
|
|
35
|
+
totals[i] += duration // for use with a daily total row
|
|
36
|
+
row.Total += duration // accumulate the projects weekly totoal
|
|
37
|
+
}
|
|
38
|
+
reportData.push(row)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const totalRow = {
|
|
42
|
+
projectName: 'Total',
|
|
43
|
+
Total: 0
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < totals.length; i++) {
|
|
47
|
+
const seconds = totals[i]
|
|
48
|
+
const date = dayjs().startOf('week').add(i, 'days').format('ddd MM-DD')
|
|
49
|
+
totalRow[date] = formatDuration(seconds * 1000)
|
|
50
|
+
totalRow.Total += seconds // accumulate the projects weekly totoal
|
|
51
|
+
}
|
|
52
|
+
reportData.push(totalRow)
|
|
53
|
+
|
|
54
|
+
for (const project of reportData) {
|
|
55
|
+
project.Total = formatDuration(project.Total * 1000)
|
|
56
|
+
}
|
|
57
|
+
console.table(reportData)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// TODO figure out what to do with these
|
|
61
|
+
function displayReportJson (data) {
|
|
62
|
+
const json = []
|
|
63
|
+
data.map(project => {
|
|
64
|
+
const startOfWeek = dayjs().startOf('week')
|
|
65
|
+
project.totals.slice(0, 7).map((total, i) => {
|
|
66
|
+
const duration = dayjs.duration(total, 'milliseconds').format('H[h] m[m]')
|
|
67
|
+
const day = startOfWeek.add(i, 'days').format('YYYY-MM-DD')
|
|
68
|
+
// i == 7 ? console.info(`\tTotal : ${duration}`) : console.info(`\t ${day} - ${duration}`);
|
|
69
|
+
// TODO revise this format - maybe nested
|
|
70
|
+
const entry = {
|
|
71
|
+
project: project.title.project,
|
|
72
|
+
date: day,
|
|
73
|
+
duration,
|
|
74
|
+
total
|
|
75
|
+
}
|
|
76
|
+
json.push(entry)
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
console.log(JSON.stringify(json))
|
|
80
|
+
console.table(json)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// TODO figure out what do to with these
|
|
84
|
+
function displayReportText (data) {
|
|
85
|
+
data.map(project => {
|
|
86
|
+
console.info(project.title.project)
|
|
87
|
+
const startOfWeek = dayjs().startOf('week')
|
|
88
|
+
project.totals.map((total, i) => {
|
|
89
|
+
// TODO see https://runkit.com/6022b36c23da0600130851a0/6022b36c7614ed001ad11853 for formatting options
|
|
90
|
+
const dur = dayjs.duration(total, 'milliseconds')
|
|
91
|
+
const hours = (dur.days() * 24) + dur.hours()
|
|
92
|
+
const duration = `${hours}h ${dur.minutes()}m `
|
|
93
|
+
// let duration = `${hours}h ${dur.minutes()}m ${dur.seconds()}s`
|
|
94
|
+
const day = startOfWeek.add(i, 'days').format('ddd MMM D')
|
|
95
|
+
i == 7 ? console.info(`\tTotal : ${duration}`) : console.info(`\t ${day} - ${duration}`)
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { workspaces } from './workspaces/index.mjs'
|
|
2
|
+
|
|
3
|
+
export const command = 'workspace <command>'
|
|
4
|
+
export const desc = 'Manage workspaces'
|
|
5
|
+
|
|
6
|
+
export const builder = function (yargs) {
|
|
7
|
+
return yargs.command(workspaces)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const handler = function (argv) {}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Client from '../../client.js'
|
|
2
|
+
|
|
3
|
+
export const command = 'list'
|
|
4
|
+
export const desc = 'Lists workspaces'
|
|
5
|
+
|
|
6
|
+
export const builder = {
|
|
7
|
+
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const handler = async function (argv) {
|
|
11
|
+
const client = Client()
|
|
12
|
+
const workspaces = await client.workspaces.list()
|
|
13
|
+
console.log(workspaces)
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@beauraines/toggl-cli",
|
|
3
|
+
"version": "0.10.5",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"toggl": "./cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "jest",
|
|
11
|
+
"lint": "eslint . ./**/*.js ./**/*.js",
|
|
12
|
+
"lint:fix": "eslint . ./**/*.js ./**/*.mjs --fix",
|
|
13
|
+
"release": "standard-version",
|
|
14
|
+
"should-release": "should-release"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"toggl",
|
|
19
|
+
"cli",
|
|
20
|
+
"time",
|
|
21
|
+
"tracking"
|
|
22
|
+
],
|
|
23
|
+
"author": "Beau Raines <beau.raines@gmail.com>",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/beauraines/toggl-cli-node.git"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/beauraines/toggl-cli-node/issues"
|
|
30
|
+
},
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"dayjs": "^1.11.6",
|
|
34
|
+
"dotenv": "^16.0.3",
|
|
35
|
+
"open": "^8.4.0",
|
|
36
|
+
"toggl-client": "^3.1.1",
|
|
37
|
+
"yargs": "^17.6.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"eslint": "^8.34.0",
|
|
41
|
+
"eslint-config-standard": "^17.0.0",
|
|
42
|
+
"eslint-plugin-import": "^2.27.5",
|
|
43
|
+
"eslint-plugin-jest": "^27.2.1",
|
|
44
|
+
"eslint-plugin-n": "^15.6.1",
|
|
45
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
46
|
+
"jest": "^29.4.3",
|
|
47
|
+
"should-release": "^1.2.0",
|
|
48
|
+
"standard-version": "^9.5.0"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import Client from 'toggl-client'
|
|
2
|
+
import { displayTimeEntry } from '../utils.js'
|
|
3
|
+
import dotenv from 'dotenv'
|
|
4
|
+
dotenv.config()
|
|
5
|
+
|
|
6
|
+
async function main () {
|
|
7
|
+
const client = new Client()
|
|
8
|
+
const currentTimeEntry = await client.timeEntries.current()
|
|
9
|
+
await displayTimeEntry(currentTimeEntry)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
main()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import togglClient from 'toggl-client'
|
|
2
|
+
import dotenv from 'dotenv'
|
|
3
|
+
const client = togglClient()
|
|
4
|
+
dotenv.config()
|
|
5
|
+
|
|
6
|
+
async function main () {
|
|
7
|
+
const workspaces = await client.workspaces.list()
|
|
8
|
+
|
|
9
|
+
const workspace = workspaces[0]
|
|
10
|
+
|
|
11
|
+
console.info('Workspace: ' + workspace.name)
|
|
12
|
+
console.info('id: ' + workspace.id)
|
|
13
|
+
|
|
14
|
+
const projects = await client.workspaces.projects(workspace.id)
|
|
15
|
+
|
|
16
|
+
const activeProjects = projects.filter(x => x.active)
|
|
17
|
+
console.info(`Found ${activeProjects.length} projects`)
|
|
18
|
+
activeProjects.map(p => { console.log(p.name + ' ' + p.id) })
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
main()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import togglClient from 'toggl-client'
|
|
2
|
+
import dayjs from 'dayjs'
|
|
3
|
+
import yargs from 'yargs'
|
|
4
|
+
import { hideBin } from 'yargs/helpers'
|
|
5
|
+
import { defaultWorkspaceId, defaultProjectId } from '../utils.js'
|
|
6
|
+
import dotenv from 'dotenv'
|
|
7
|
+
dotenv.config()
|
|
8
|
+
|
|
9
|
+
const client = togglClient()
|
|
10
|
+
|
|
11
|
+
const options = yargs(hideBin(process.argv))
|
|
12
|
+
.usage('Usage: $0 -p <project_id> -w <workspace_id> [time entry description]')
|
|
13
|
+
.option('p', { alias: 'projectId', describe: 'project id', type: 'string', demandOption: false })
|
|
14
|
+
.option('w', { alias: 'workspaceId', describe: 'workspace id', type: 'number', demandOption: false })
|
|
15
|
+
.argv
|
|
16
|
+
|
|
17
|
+
async function main () {
|
|
18
|
+
// console.debug(JSON.stringify(options))
|
|
19
|
+
// TODO validate options
|
|
20
|
+
const description = options._.join(' ')
|
|
21
|
+
// TODO check that description was provided or provide a default
|
|
22
|
+
const params = { description }
|
|
23
|
+
params.workspaceId = options.workspaceId || defaultWorkspaceId
|
|
24
|
+
params.projectId = options.projectId || defaultProjectId
|
|
25
|
+
// console.debug(params)
|
|
26
|
+
const timeEntry = await createTimeEntry(params)
|
|
27
|
+
console.info(`Started ${timeEntry.description} with project id ${timeEntry.pid}`)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function createTimeEntry (params) {
|
|
31
|
+
// const client = Client()
|
|
32
|
+
const timeEntry = await client.timeEntries.create(
|
|
33
|
+
{
|
|
34
|
+
description: params.description,
|
|
35
|
+
workspace_id: +params.workspaceId,
|
|
36
|
+
project_id: +params.projectId,
|
|
37
|
+
start: dayjs().toISOString(),
|
|
38
|
+
duration: -1 * dayjs().unix(),
|
|
39
|
+
created_with: 'My app',
|
|
40
|
+
at: dayjs().toISOString()
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
// console.log(timeEntry)
|
|
44
|
+
return timeEntry
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
main()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import togglClient from 'toggl-client'
|
|
2
|
+
import dayjs from 'dayjs'
|
|
3
|
+
import duration from 'dayjs/plugin/duration.js'
|
|
4
|
+
import relativeTime from 'dayjs/plugin/relativeTime.js'
|
|
5
|
+
import dotenv from 'dotenv'
|
|
6
|
+
dayjs.extend(relativeTime)
|
|
7
|
+
dayjs.extend(duration)
|
|
8
|
+
dotenv.config()
|
|
9
|
+
|
|
10
|
+
const client = togglClient()
|
|
11
|
+
|
|
12
|
+
async function main () {
|
|
13
|
+
const currentTimeEntry = await client.timeEntries.current()
|
|
14
|
+
// TODO check if there is actually something to stop
|
|
15
|
+
const stopped = await client.timeEntries.stop(currentTimeEntry.id)
|
|
16
|
+
|
|
17
|
+
const duration = dayjs.duration({ seconds: stopped.duration }).humanize()
|
|
18
|
+
console.log(`Stopped ${stopped.description} after ${duration}`)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
main()
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import togglClient from 'toggl-client'
|
|
2
|
+
import dayjs from 'dayjs'
|
|
3
|
+
import duration from 'dayjs/plugin/duration.js'
|
|
4
|
+
import relativeTime from 'dayjs/plugin/relativeTime.js'
|
|
5
|
+
import dotenv from 'dotenv'
|
|
6
|
+
dayjs.extend(relativeTime)
|
|
7
|
+
dayjs.extend(duration)
|
|
8
|
+
dotenv.config()
|
|
9
|
+
|
|
10
|
+
const client = togglClient()
|
|
11
|
+
|
|
12
|
+
async function getWorkspace () {
|
|
13
|
+
const workspaces = await client.workspaces.list()
|
|
14
|
+
return workspaces[0]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function main () {
|
|
18
|
+
const workspace = await getWorkspace()
|
|
19
|
+
const params = { start_date: dayjs().startOf('day').toISOString() }
|
|
20
|
+
|
|
21
|
+
const timeEntries = await client.timeEntries.list(params)
|
|
22
|
+
|
|
23
|
+
// [
|
|
24
|
+
// {
|
|
25
|
+
// id: 2463026053,
|
|
26
|
+
// guid: 'dba205a234d78492f3f7510c0c00b790',
|
|
27
|
+
// wid: 403916,
|
|
28
|
+
// pid: 174558624,
|
|
29
|
+
// billable: false,
|
|
30
|
+
// start: '2022-04-22T17:38:27+00:00',
|
|
31
|
+
// stop: '2022-04-22T18:07:50+00:00',
|
|
32
|
+
// duration: 1763,
|
|
33
|
+
// description: 'recommendations',
|
|
34
|
+
// duronly: false,
|
|
35
|
+
// at: '2022-04-22T18:07:51+00:00',
|
|
36
|
+
// uid: 530321
|
|
37
|
+
// },
|
|
38
|
+
// {
|
|
39
|
+
// id: 2463068736,
|
|
40
|
+
// guid: 'e9a444f99da3a09de1f16d242546e18e',
|
|
41
|
+
// wid: 403916,
|
|
42
|
+
// pid: 174558624,
|
|
43
|
+
// billable: false,
|
|
44
|
+
// start: '2022-04-22T18:16:01+00:00',
|
|
45
|
+
// stop: '2022-04-22T18:33:44+00:00',
|
|
46
|
+
// duration: 1063,
|
|
47
|
+
// description: 'profile updates',
|
|
48
|
+
// duronly: false,
|
|
49
|
+
// at: '2022-04-22T18:33:44+00:00',
|
|
50
|
+
// uid: 530321
|
|
51
|
+
// }
|
|
52
|
+
// ]
|
|
53
|
+
|
|
54
|
+
console.log(timeEntries)
|
|
55
|
+
let projects = []
|
|
56
|
+
timeEntries.map(e => {
|
|
57
|
+
console.log(`${e.start} ${e.description} ${e.duration}`)
|
|
58
|
+
// find unique projects
|
|
59
|
+
projects.push(e.pid)
|
|
60
|
+
// filter for each project
|
|
61
|
+
// reduce() or _.groupBy() or groupBy()
|
|
62
|
+
// sum duration
|
|
63
|
+
// 2022-04-22T18:53:24+00:00 Time tracking - cli 52
|
|
64
|
+
// 2022-04-22T18:54:15+00:00 Time tracking - cli 225
|
|
65
|
+
// 2022-04-22T18:58:00+00:00 MCP All Hands 3191
|
|
66
|
+
// 2022-04-22T19:56:37+00:00 Time tracking - cli 303
|
|
67
|
+
// 2022-04-22T20:01:39+00:00 Time tracking - cli -1650657699
|
|
68
|
+
})
|
|
69
|
+
projects = [...new Set(projects)]
|
|
70
|
+
|
|
71
|
+
const foo = []
|
|
72
|
+
projects.map(p => {
|
|
73
|
+
let total = 0
|
|
74
|
+
timeEntries.filter(x => x.pid == p).map(e => {
|
|
75
|
+
if (e.duration >= 0) {
|
|
76
|
+
total += e.duration // UNLESS e.duration is less than zero - meaning currently running
|
|
77
|
+
} else {
|
|
78
|
+
const startTime = dayjs.unix(e.duration * -1)
|
|
79
|
+
console.log(`computed ${startTime}`)
|
|
80
|
+
console.log(`timeentry.start ${e.start}`)
|
|
81
|
+
const duration = dayjs().diff(startTime, 's')
|
|
82
|
+
total += duration
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
foo.push({ [p]: total })
|
|
86
|
+
})
|
|
87
|
+
console.log(foo)
|
|
88
|
+
|
|
89
|
+
console.log(timeEntries[0])
|
|
90
|
+
console.log(timeEntries[timeEntries.length - 1])
|
|
91
|
+
|
|
92
|
+
// ! Not supported in Node
|
|
93
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/groupBy
|
|
94
|
+
// let result = timeEntries.groupBy( ({ pid }) => pid );
|
|
95
|
+
// console.log(result);
|
|
96
|
+
|
|
97
|
+
// displayReportText(weeklyReport.data);
|
|
98
|
+
// displayReportJson(weeklyReport.data);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
main()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import togglClient from 'toggl-client'
|
|
2
|
+
import dayjs from 'dayjs'
|
|
3
|
+
import duration from 'dayjs/plugin/duration.js'
|
|
4
|
+
import relativeTime from 'dayjs/plugin/relativeTime.js'
|
|
5
|
+
|
|
6
|
+
import dotenv from 'dotenv'
|
|
7
|
+
dayjs.extend(relativeTime)
|
|
8
|
+
dayjs.extend(duration)
|
|
9
|
+
|
|
10
|
+
const client = togglClient()
|
|
11
|
+
dotenv.config()
|
|
12
|
+
async function getWorkspace () {
|
|
13
|
+
const workspaces = await client.workspaces.list()
|
|
14
|
+
return workspaces[0]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function main () {
|
|
18
|
+
const workspace = await getWorkspace()
|
|
19
|
+
const params = { since: dayjs().startOf('week').toISOString() }
|
|
20
|
+
const weeklyReport = await client.reports.weekly(workspace.id, params)
|
|
21
|
+
displayReportText(weeklyReport.data)
|
|
22
|
+
// displayReportJson(weeklyReport.data);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
main()
|
|
26
|
+
|
|
27
|
+
function displayReportJson (data) {
|
|
28
|
+
const json = []
|
|
29
|
+
data.map(project => {
|
|
30
|
+
const startOfWeek = dayjs().startOf('week')
|
|
31
|
+
project.totals.slice(0, 7).map((total, i) => {
|
|
32
|
+
const duration = dayjs.duration(total, 'milliseconds').format('H[h] m[m]')
|
|
33
|
+
const day = startOfWeek.add(i, 'days').format('YYYY-MM-DD')
|
|
34
|
+
// i == 7 ? console.info(`\tTotal : ${duration}`) : console.info(`\t ${day} - ${duration}`);
|
|
35
|
+
// TODO revise this format - maybe nested
|
|
36
|
+
const entry = {
|
|
37
|
+
project: project.title.project,
|
|
38
|
+
date: day,
|
|
39
|
+
duration
|
|
40
|
+
}
|
|
41
|
+
json.push(entry)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
console.log(JSON.stringify(json))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function displayReportText (data) {
|
|
48
|
+
data.map(project => {
|
|
49
|
+
console.info(project.title.project)
|
|
50
|
+
const startOfWeek = dayjs().startOf('week')
|
|
51
|
+
project.totals.map((total, i) => {
|
|
52
|
+
const duration = dayjs.duration(total, 'milliseconds').format('H[h] m[m]')
|
|
53
|
+
const day = startOfWeek.add(i, 'days').format('ddd MMM D')
|
|
54
|
+
i == 7 ? console.info(`\tTotal : ${duration}`) : console.info(`\t ${day} - ${duration}`)
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
}
|