Using the Harvest API to Build Custom Reports
We use Harvest for time-tracking, which is simple and easy to use.
We use Harvest for time-tracking, which is simple and easy to use. For most users the tools on the website will be enough, but if you need to create a custom report or want to share data between Harvest and another application, you can use the Harvest API. I'm primarily a PHP guy, so using the Harvest PHP library makes it even easier. This post will demonstrate how to use this to create a custom reporting tool.
In our case, we wanted to see how many hours a given user spent on weekends over a given time period.
<?php
/**
* @file
* This calculates the number of hours logged on weekends during a given period.
*/
/**
* Set your credentials here. You must be an admin on the Harvest account for
* this to work properly. You may want to store this in a separate file and include
* that file here.
* include('settings.php');
*/
$harvest_user = ''; // Your Harvest username, usually an email address
$harvest_pass = ''; // Your Harvest password
$harvest_account = ''; // The {myaccount} portion of your Harvest url: {myaccount}.harvestapp.com
// date_default_timezone_set('America/Los_Angeles'); // Set your timezone if it is not set in your php.ini
// Include the HarvestAPI class
require_once 'HarvestAPI.php';
// Register the HarvestAPI autoloader
spl_autoload_register(array('HarvestAPI', 'autoload'));
$harvestAPI = new HarvestAPI();
$harvestAPI->setUser($harvest_user);
$harvestAPI->setPassword($harvest_pass);
$harvestAPI->setAccount($harvest_account);
That sets up our object, now we can make calls against it like so:
<?php
$result = $harvestAPI->getUsers();
// If Harvest returns successful
if ($result->code == 200) {
$people = $result->data;
}
?>
Since we only wanted entries that were on a weekend, I made a convenience function to help keep the resulting html a little more tidy.
/**
* @param int from
* A date as recongized by strtotime for report start date.
* @param int to
* A date as recongized by strtotime for report end date.
* @param int user_id
* The Harvest User's id.
* @param object
* The Harvest API object, with user already logged in.
* @return array
* The entries that were on a weekend during the time period.
*/
function getWeekendEntriesForPeriod($from, $to, $user_id, $harvestAPI) {
$range = new Harvest_Range(date('Ymd', strtotime($from)), date('Ymd', strtotime($to)));
$activity = $harvestAPI->getUserEntries($user_id, $range);
$entries = array();
$hours_per_week = array();
foreach ($activity->data as $entry) {
// We also want to calculate the total hours spent during that week
$week_year = date('W-o', strtotime($entry->spent_at)); // Uses ISO-8601, weeks are Monday - Sunday
if (!isset($hours_per_week[$week_year])) {
$hours_per_week[$week_year] = 0;
}
$hours_per_week[$week_year] += $entry->hours;
// If the entry is during a weekend
$day_of_week = date('w', strtotime($entry->spent_at));
// 0 = Sunday, 6 = Saturday
if ($day_of_week == 0 || $day_of_week == 6) {
$entries[] = $entry;
}
}
// Loop through all the entries we are going to return and set our calculated hours_per_week
foreach ($entries as $key => $entry) {
$week_year = date('W-o', strtotime($entry->spent_at));
$entries[$key]->hours_per_week = $hours_per_week[$week_year];
}
return $entries;
}
Finally, I built a simple persistent GET form to make a simple interface for our report. I'm also using the HTML5 date input type with a handy jQuery popup calendar.
<html>
<head>
<title>Weekend Hours Calculator</title>
<script type="text/javascript" language="javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="https://cdn.jquerytools.org/1.2.6/form/jquery.tools.min.js"></script>
<script type="text/javascript" language="javascript">
$(document).ready(function() {
$(":date").dateinput();
});
</script>
<link type="text/css" rel="stylesheet" href="styles.css" />
</head>
<body>
<?php
// Display form for report
$from = isset($_GET['from']) ? $_GET['from'] : date('Y-m-d', time() - 86400);
$to = isset($_GET['to']) ? $_GET['to'] : date('Y-m-d');
$options = array();
foreach ($people as $person) {
// Only show active people
if ($person->is_active == 'true') {
$option_html = '<option value="' . $person->id . '"';
if (isset($_GET['user_id']) && $_GET['user_id'] == $person->id) {
$option_html .= ' selected="selected"';
}
$option_html .= '>' . $person->first_name . ' ' . $person->last_name . '</option>';
$options[] = $option_html;
}
} ?>
<form name="report" method="GET">
<label>Search:</label> <select name="user_id">
<?php implode("\n", $options); ?>
</select> <input type="date" name="from" value="<?php echo $from; ?>" /> <input type="date" name="to" value="<?php echo $to; ?>" /> <input type="submit" value="Get Report" />
</form>
<?php
// If we are running a report
if (isset($_GET['user_id']) && isset($_GET['from']) && isset($_GET['to'])) {
$entries = getWeekendEntriesForPeriod($_GET['from'], $_GET['to'], $_GET['user_id'], $harvestAPI);
if (count($entries)) {
echo '<table><tr><th>Hours</th><th>Date</th><th>Weekly Hours</th><th>Details</th>';
$hours = 0;
foreach ($entries as $entry) {
echo '<tr><td class="right">' . $entry->hours . '</td><td>' . date('l, M j, Y', strtotime($entry->spent_at)) . '</td><td>' . $entry->hours_per_week . '</td><td>' . $entry->notes . '</td></tr>';
$hours += $entry->hours;
}
echo '<tr><th class="right">' . $hours .'</th><th class="left" colspan="3">Total Weekend Hours</th></tr></table>';
}
//
else {
echo '<div class="noresults">No hours reported on a weekend for this user during the time period.</div>';
}
}
?>
</body>
</html>
And the end result is a simple but informative table: