Skip to content

Instantly share code, notes, and snippets.

@megawertz
Last active January 12, 2016 18:59
Show Gist options
  • Save megawertz/5587042 to your computer and use it in GitHub Desktop.
Save megawertz/5587042 to your computer and use it in GitHub Desktop.
Gets unbilled hours from Freshbooks and formats them into a CSV for Panic's Status Board app. This only shows unbilled totals per project. It suits my needs but yours will probably differ.
// Get unbilled hours from Freshbooks and show them in Panic's Status Board
// This assumes A LOT and is built for my usage. YMMV.
var sys = require('sys');
var fs = require('fs');
var exec = require('child_process').exec;
var parseString = require('xml2js').parseString;
var projListChild, timeEntryListChild;
var apiToken = "yourkeyhere";
var apiEndPoint = "https://yourendpointurl.freshbooks.com/api/2.1/xml-in";
// Assuming that I won't have more than 25 active projects and 50 unbilled entries
var projectListXMLRequest = '<?xml version="1.0" encoding="utf-8"?><request method="project.list"></request>';
var timeEntryListXMLRequest = '<?xml version="1.0" encoding="utf-8"?><request method="time_entry.list"><per_page>50</per_page></request>';
var curlBaseCMD = 'curl -u ' + apiToken + ':X ' + apiEndPoint + ' -d ';
var statusBoardOutputFile = "/Path/To/Your/Dropbox/Public/StatusBoard/fb.csv"
projListChild = exec(curlBaseCMD + "'" + projectListXMLRequest + "'", function(error, stdout, stderr) {
var projects = new Array();
// Grab all active projects and put them in the array
if(error === null) {
parseString(stdout, function (err, result) {
var response = result.response;
var allProjects = response.projects[0].project;
for(var i = 0 ; i < allProjects.length ; i++) {
var tmp = {};
tmp.projectName = allProjects[i].name[0];
tmp.projectID = allProjects[i].project_id[0];
tmp.rate = allProjects[i].rate[0];
projects.push(tmp);
}
});
}
else {
console.log(error);
}
// This grabs the last 50 entries and includes active and inactive projects
// Could filter out inactive projects here, did it in generateStatusBoard() for some reason
timeEntryListChild = exec(curlBaseCMD + "'" + timeEntryListXMLRequest + "'", function(error, stdout, stderr) {
if(error === null) {
var totals = {};
parseString(stdout, function (err, result) {
var response = result.response;
var allTimeEntries = response.time_entries[0].time_entry;
console.log(allTimeEntries);
for(var i = 0 ; i < allTimeEntries.length ; i++) {
if(allTimeEntries[i].billed[0] === '0') {
if(totals[allTimeEntries[i].project_id[0]] === undefined) {
totals[allTimeEntries[i].project_id[0]] = 0;
}
totals[allTimeEntries[i].project_id[0]] += parseFloat(allTimeEntries[i].hours[0]);
}
}
generateStatusBoard(totals, projects);
});
}
else {
console.log(error);
}
});
});
// Only gets values for active projects, no limit
function generateStatusBoard(entryTotals, projects) {
//console.log("Generating Status board");
// just uses CSV
var csv = "80%,20%\n";
for(var i = 0 ; i < projects.length ; i++) {
csv += projects[i].projectName + ",";
var id = projects[i].projectID;
if(entryTotals[id] !== undefined) {
csv += entryTotals[id] + "\n";
}
else {
csv += 0 + "\n";
}
}
fs.writeFile(statusBoardOutputFile, csv, function(err) {
if(err) {
console.log(err);
}
});
//console.log(csv);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment