Using the Harvest API to Build Custom Reports

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:

Filed under:

Comments

As the beneficiary of some of Jonathan's Harvest work, I've been super impressed with the interaction he's been able to put together. It's like totally Internet 2.0!

I'm new to Harvest, but I do know php. My question might be silly, but where do you put this code in order to do what you want? Can you add it to the Harvest account someone?

Harvest won't host the code for you – I believe you'll need your own web server. Alternatively, you could write a non-web program to run from the command-line.

Dear Jonathan:

Great post. I am currently working on generating three reports using the Harvest API and if you wouldn't mind I would like to talk to you some about possible ways to get the data necessary. I already have found ways to get some of the data required but prior to building my functions and in the long term tables to display the data I am trying to figure out what methods I should look at to pull the data necessary. Thanks for the help in advance.

David

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>, <cpp>, <java>, <php>. The supported tag styles are: <foo>, [foo].
  • Web page addresses and email addresses turn into links automatically.
  • Lines and paragraphs break automatically.

Ready for transformation?