Created
December 23, 2023 15:38
-
-
Save alex-streza/5ddbf80c38637b374349db6a7ef7c85b to your computer and use it in GitHub Desktop.
Node.js script to fetch Toggl time entries via the official API, organizing data into a monthly CSV timesheet.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fetch = require('node-fetch'); | |
const jsonexport = require('jsonexport'); | |
const fs = require('fs').promises; | |
const path = require('path'); | |
const API_URL = process.env.API_URL; | |
const EMAIL = process.env.EMAIL; | |
const PASSWORD = process.env.PASSWORD; | |
const OUTPUT_PATH = process.env.OUTPUT_PATH; | |
const NAME = process.env.NAME; | |
const config = { | |
headers: { | |
"Content-Type": "application/json", | |
"Authorization": `Basic ${Buffer.from(`${EMAIL}:${PASSWORD}`).toString('base64')}`, | |
}, | |
}; | |
const months = [ | |
"january", "february", "march", "april", "may", "june", | |
"july", "august", "september", "october", "november", "december", | |
]; | |
const getStartEndMonthDates = () => { | |
const today = new Date(); | |
const firstDay = new Date(today.getFullYear(), today.getMonth(), 1); | |
const lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0); | |
return [firstDay, lastDay]; | |
}; | |
const getHoursBetweenDates = (start, end) => { | |
const diff = end.getTime() - start.getTime(); | |
return diff / (1000 * 60 * 60); | |
}; | |
const fetchData = async (url) => { | |
try { | |
const response = await fetch(url, config); | |
if (!response.ok) { | |
throw new Error(`HTTP error! Status: ${response.status}`); | |
} | |
return await response.json(); | |
} catch (error) { | |
throw new Error(error.message); | |
} | |
}; | |
const writeToFile = async (filePath, data) => { | |
try { | |
await fs.writeFile(filePath, data); | |
console.log(`File saved at: ${filePath}`); | |
} catch (error) { | |
throw new Error(error.message); | |
} | |
}; | |
(async () => { | |
try { | |
const [start] = getStartEndMonthDates(); | |
const currentMonth = months[start.getMonth()]; | |
const projectsResponse = await fetchData(`${API_URL}/projects`); | |
const projects = projectsResponse.data; | |
const entriesResponse = await fetchData(`${API_URL}/time_entries?since=${Math.floor(start.getTime() / 1000)}`); | |
const entries = []; | |
for (const entry of entriesResponse) { | |
const { start, stop, description, pid } = entry; | |
const project = projects.find((project) => project.id === pid); | |
entries.push({ | |
date: start.split("T")[0].split("-").reverse().join("/"), | |
project: project ? project.name : "Unknown", | |
person: NAME, | |
task: description, | |
hours: getHoursBetweenDates(new Date(start), new Date(stop)), | |
}); | |
} | |
jsonexport(entries, async (err, csv) => { | |
if (err) return console.error(err); | |
const filePath = path.join(OUTPUT_PATH, `${currentMonth}.csv`); | |
await writeToFile(filePath, csv); | |
}); | |
} catch (e) { | |
console.error(e); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment