├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── Vagrantfile ├── composer.json ├── phpunit.xml.dist ├── src └── RescueTime │ ├── .Activity.php.swp │ ├── Activity.php │ ├── Client.php │ ├── DailyReport.php │ ├── HttpClient.php │ └── RequestQueryParameters.php └── tests ├── RescueTime └── Tests │ ├── ClientTest.php │ └── Fakes │ ├── daily_report.json │ ├── interval.json │ ├── member.json │ └── rank.json └── bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Ingore Vagrant stuff 4 | *.box 5 | .cache/ 6 | .vagrant 7 | .bashrc 8 | .bash_history 9 | .sudo_as_admin_successful 10 | 11 | # Ignore Composer stuff 12 | .composer/ 13 | vendor/ 14 | composer.lock 15 | 16 | # Ignore IDE stuff 17 | .idea 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - 7.0 8 | 9 | before_script: 10 | - composer install --dev 11 | 12 | script: 13 | - phpunit -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Getting Started (Vagrant Setup) 2 | 3 | 1. Install Git 4 | 2. Install VirtualBox: https://www.virtualbox.org/wiki/Downloads 5 | 3. Install Vagrant: http://www.vagrantup.com/ (version 1.2.2+ or later) 6 | 4. Open a terminal 7 | 5. Clone the project: `git clone https://github.com/borivojevic/rescuetime-api-php.git` 8 | 6. Enter the project directory: `cd rescuetime-api-php` 9 | 10 | ### Using Vagrant 11 | 12 | When you're ready to start working, boot the VM: 13 | 14 | ``` 15 | vagrant up 16 | ``` 17 | 18 | The first time you do this Vagrant will have to download and install VM image which can take ~15 minutes or more depending on internet connection speed. 19 | 20 | When machine boots up ssh into it by typing: 21 | 22 | ``` 23 | vagrant ssh 24 | # password: vagrant 25 | ``` 26 | 27 | **On Windows**: Use : ssh vagrant@127.0.0.1 -p 2222 28 | 29 | 30 | ### Making Changes 31 | 32 | The application code is found in vagrant home directory at /home/vagrant 33 | 34 | The very first time you'll have to install application dependencies: 35 | 36 | ``` 37 | sudo composer self-update 38 | composer install 39 | ``` 40 | 41 | Basic Workflow: Run vagrant machine >> Update repository >> Edit >> Send GitHub pull request 42 | 43 | 1. Update repository 44 | 1. `cd /home/vagrant` 45 | 1. `git checkout master` 46 | 1. `git pull` 47 | 1. Make changes to RescueTime API 48 | 1. `cd /home/vagrant` 49 | 1. (Make changes) 50 | 1. `git commit ...` 51 | 1. (Make sure all tests pass. See [testing](#testing)) 52 | 1. `git push` 53 | 1. Submit pull request to master repository 54 | 1. [Using pull requests](https://help.github.com/articles/using-pull-requests) 55 | 1. [Creating a pull request](https://help.github.com/articles/creating-a-pull-request) 56 | 57 | ### Testing 58 | 59 | To run unit test suite type: 60 | 61 | ``` 62 | phpunit 63 | ``` 64 | 65 | To run coding standard tests type: 66 | 67 | ``` 68 | phpcs --standard=PSR2 src/ 69 | phpcs --standard=PSR2 tests/ 70 | ``` 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Mirko Borivojevic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/borivojevic/rescuetime-api-php.png?branch=master)](https://travis-ci.org/borivojevic/rescuetime-api-php) 2 | [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/borivojevic/rescuetime-api-php/badges/quality-score.png?s=688bb689ba8d980c06526783e139e8efc13528c5)](https://scrutinizer-ci.com/g/borivojevic/rescuetime-api-php/) 3 | [![Code Coverage](https://scrutinizer-ci.com/g/borivojevic/rescuetime-api-php/badges/coverage.png?s=0e6ffb9bfbbf1e31aa7fd2ff8d19657eda881129)](https://scrutinizer-ci.com/g/borivojevic/rescuetime-api-php/) 4 | [![Latest Stable Version](https://poser.pugx.org/borivojevic/rescuetime/v/stable.png)](https://packagist.org/packages/borivojevic/rescuetime) 5 | 6 | rescuetime-api-php 7 | ======== 8 | 9 | PHP wrapper library for [RescueTime][] API 10 | 11 | At this point RescueTime API provides single endpoint to fetch detailed and complicated data. The data is read-only through the API. 12 | 13 | Initial release of RescueTime API is targeted at bringing developers the prepared and pre-organized data structures already familiar through the reporting views of www.rescuetime.com. 14 | Keep in mind this is a draft interface, and may change in the future. RescueTime do intend to version the interfaces though, so it is likely forward compatible. 15 | 16 | ### Installation ### 17 | 18 | Recommend way to install this package with [Composer][]. Add borivojevic/rescuetime-api-php to your composer.json file. 19 | 20 | ``` json 21 | { 22 | "require": { 23 | "borivojevic/rescuetime": "2.*" 24 | } 25 | } 26 | ``` 27 | 28 | To install composer run: 29 | 30 | ``` 31 | curl -s http://getcomposer.org/installer | php 32 | ``` 33 | 34 | To install composer dependences run: 35 | 36 | ``` 37 | php composer.phar install 38 | ``` 39 | 40 | You can autoload all dependencies by adding this to your code: 41 | 42 | ``` 43 | require 'vendor/autoload.php'; 44 | ``` 45 | 46 | ### Usage ### 47 | 48 | The main entry point of the library is the `RescueTime\Client` class. API methods require to be signed with valid `api_key` parameter which you have to provide as a first argument of the constructor. You can obtain RescueTime API key on [API Key Management][] console page. 49 | 50 | ``` php 51 | getActivities(new Params(['perspective' => 'rank'])); 60 | 61 | foreach ($activities as $activity) { 62 | echo $activity->getActivityName(); 63 | echo $activity->getProductivity(); 64 | } 65 | 66 | // Fetch activities for past week 67 | $activities = $client->getActivities( 68 | new Params([ 69 | 'perspective' => 'interval', 70 | 'resolution_time' => 'day', 71 | 'restrict_begin' => new \DateTime("-6 day"), 72 | 'restrict_end' => new \DateTime("today") 73 | ]) 74 | ); 75 | 76 | // Fetch productivity data grouped by activity 77 | $activities = $client->getActivities( 78 | new Params([ 79 | 'perspective' => 'interval', 80 | 'resolution_time' => 'day', 81 | 'restrict_begin' => new \DateTime("-6 day"), 82 | 'restrict_end' => new \DateTime("today"), 83 | 'restrict_kind' => 'activity' 84 | ]) 85 | ); 86 | 87 | // Fetch productivity data grouped by category 88 | $activities = $client->getActivities( 89 | new Params([ 90 | 'perspective' => 'interval', 91 | 'resolution_time' => 'day', 92 | 'restrict_begin' => new \DateTime("-6 day"), 93 | 'restrict_end' => new \DateTime("today"), 94 | 'restrict_kind' => 'category' 95 | ]) 96 | ); 97 | 98 | // Fetch daily productivity report data for past two weeks 99 | $daily_summary = $client->getDailySummary(); 100 | 101 | foreach ($daily_summary as $day_summary) { 102 | echo $day_summary->getTotalDurationFormatted(); 103 | echo $day_summary->getVeryDistractingHours(); 104 | echo $day_summary->getVeryDistractingDurationFormatted(); 105 | } 106 | ``` 107 | 108 | You can build more complex queries and filter down the data by providing other query parameters: 109 | 110 | ``` php 111 | $client->getActivities( 112 | new Params([ 113 | "perspective" => , 114 | "resolution_time" => , 115 | "restrict_group" => , 116 | "restrict_user" => , 117 | "restrict_begin" => <\DateTime>, 118 | "restrict_end" => <\DateTime>, 119 | "restrict_kind" => , 120 | "restrict_project" => , 121 | "restrict_thing" => , 122 | "restrict_thingy" => 123 | ]) 124 | ); 125 | ``` 126 | 127 | Each query parameter is explained in more details in official [HTTP Query Interface documentation][]. 128 | 129 | For a working example of an app build on top of rescuetime-api-php library take a look at [borivojevic/rescuetime-statusboard][]. 130 | 131 | ### Contributing ### 132 | 133 | Patches and pull requests are welcome. Take a look at [Contributing guidelines][] for further info. 134 | 135 | ### Versioning ### 136 | 137 | The library uses [Semantic Versioning][] 138 | 139 | ### Copyright and License ### 140 | 141 | The library is licensed under the MIT license. 142 | 143 | [RescueTime]: https://www.rescuetime.com 144 | [Composer]: http://getcomposer.org/ 145 | [API Key Management]: https://www.rescuetime.com/anapi/manage 146 | [HTTP Query Interface documentation]: https://www.rescuetime.com/anapi/setup/documentation#http 147 | [borivojevic/rescuetime-statusboard]: https://github.com/borivojevic/rescuetime-statusboard 148 | [Semantic Versioning]: http://semver.org/ 149 | [Contributing guidelines]: https://github.com/borivojevic/rescuetime-api-php/blob/master/CONTRIBUTING.md 150 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure("2") do |config| 5 | config.vm.box = "rescuetime-0.1.0" 6 | config.vm.box_url = "https://s3.amazonaws.com/rescuetime-api-php/rescuetime.box" 7 | 8 | # Create a private network, which allows host-only access to the machine 9 | # using a specific IP. 10 | config.vm.network :private_network, ip: "192.168.33.10" 11 | 12 | # Make it so that network access from the vagrant guest is able to 13 | # use SSH private keys that are present on the host without copying 14 | # them into the VM. 15 | config.ssh.forward_agent = true 16 | 17 | # Create a public network, which generally matched to bridged network. 18 | # Bridged networks make the machine appear as another physical device on 19 | # your network. 20 | # config.vm.network :public_network 21 | 22 | # Share an additional folder to the guest VM. The first argument is 23 | # the path on the host to the actual folder. The second argument is 24 | # the path on the guest to mount the folder. And the optional third 25 | # argument is a set of non-required options. 26 | nfs_setting = RUBY_PLATFORM =~ /darwin/ || RUBY_PLATFORM =~ /linux/ 27 | config.vm.synced_folder ".", "/home/vagrant", id: "vagrant-root", :nfs => nfs_setting 28 | 29 | # Provider-specific configuration so you can fine-tune various 30 | # backing providers for Vagrant. These expose provider-specific options. 31 | # Example for VirtualBox: 32 | # 33 | # config.vm.provider :virtualbox do |vb| 34 | # # Don't boot with headless mode 35 | # vb.gui = true 36 | # 37 | # # Use VBoxManage to customize the VM. For example to change memory: 38 | # vb.customize ["modifyvm", :id, "--memory", "1024"] 39 | # end 40 | end 41 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "borivojevic/rescuetime", 3 | "type": "library", 4 | "description": "PHP wrapper library for RescueTime API", 5 | "keywords": ["rescuetime", "api"], 6 | "homepage": "https://github.com/borivojevic/rescuetime-api-php", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Mirko Borivojevic", 11 | "email": "mirko@mirkoborivojevic.com", 12 | "homepage": "http://mirkoborivojevic.com/" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=5.4.0", 17 | "guzzle/guzzle": "3.*" 18 | }, 19 | "require-dev": { 20 | "phpunit/phpunit": "3.7.*" 21 | }, 22 | "autoload": { 23 | "psr-0": { 24 | "RescueTime": "src" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 12 | ./tests/RescueTime/ 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/RescueTime/.Activity.php.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borivojevic/rescuetime-api-php/962fa1b27f72d479d8a4fdee90d48e1ca83a86be/src/RescueTime/.Activity.php.swp -------------------------------------------------------------------------------- /src/RescueTime/Activity.php: -------------------------------------------------------------------------------- 1 | $header) { 92 | switch ($header) { 93 | case 'Rank': 94 | $properties[$key] = 'rank'; 95 | break; 96 | case 'Date': 97 | $properties[$key] = 'date'; 98 | break; 99 | case 'Person': 100 | $properties[$key] = 'person'; 101 | break; 102 | case 'Time Spent (seconds)': 103 | $properties[$key] = 'timeSpentSeconds'; 104 | break; 105 | case 'Number of People': 106 | $properties[$key] = 'numberOfPeople'; 107 | break; 108 | case 'Activity': 109 | $properties[$key] = 'activityName'; 110 | break; 111 | case 'Category': 112 | $properties[$key] = 'activityCategory'; 113 | break; 114 | case 'Document': 115 | $properties[$key] = 'document'; 116 | break; 117 | case 'Productivity': 118 | $properties[$key] = 'productivityCode'; 119 | break; 120 | } 121 | } 122 | 123 | $this->rank = null; 124 | $this->date = null; 125 | $this->person = null; 126 | $this->timeSpentSeconds = null; 127 | $this->numberOfPeople = null; 128 | $this->activityName = null; 129 | $this->activityCategory = null; 130 | $this->document = null; 131 | $this->productivityCode = null; 132 | 133 | foreach ($columns as $key => $value) { 134 | if (array_key_exists($key, $properties)) { 135 | $property = $properties[$key]; 136 | $this->$property = $value; 137 | } 138 | } 139 | } 140 | 141 | /** 142 | * Returns activity rank 143 | * 144 | * Present when perspective set to rank 145 | * 146 | * @return integer 147 | */ 148 | public function getRank() 149 | { 150 | return $this->rank; 151 | } 152 | 153 | /** 154 | * Returns activity date 155 | * 156 | * @return string 157 | */ 158 | public function getDate() 159 | { 160 | return $this->date; 161 | } 162 | 163 | /** 164 | * Returns person name 165 | * 166 | * Present when perspective set to member 167 | * 168 | * @return stirng 169 | */ 170 | public function getPerson() 171 | { 172 | return $this->person; 173 | } 174 | 175 | /** 176 | * Returns time spent (seconds) on activity 177 | * 178 | * @return integer 179 | */ 180 | public function getTimeSpentSeconds() 181 | { 182 | return $this->timeSpentSeconds; 183 | } 184 | 185 | /** 186 | * Returns number of people on activity 187 | * 188 | * Present when perspective set to interval or rank 189 | * 190 | * @return integer 191 | */ 192 | public function getNumberOfPeople() 193 | { 194 | return $this->numberOfPeople; 195 | } 196 | 197 | /** 198 | * Returns activity name 199 | * 200 | * e.g. Program used or URL visited 201 | * 202 | * @return string 203 | */ 204 | public function getActivityName() 205 | { 206 | return $this->activityName; 207 | } 208 | 209 | /** 210 | * Returns associated RescueTime activity category name 211 | * 212 | * @return string 213 | */ 214 | public function getActivityCategory() 215 | { 216 | return $this->activityCategory; 217 | } 218 | 219 | /** 220 | * Returns associated RescueTime document 221 | * 222 | * @return string 223 | */ 224 | public function getDocument() 225 | { 226 | return $this->document; 227 | } 228 | 229 | /** 230 | * Return RescueTime activiy productivity grade 231 | * 232 | * Integer number between -2 and 2 233 | * -2 (Very Distracting Time) 234 | * -1 (Distracting Time) 235 | * 0 (Neutral Time) 236 | * 1 (Productive Time) 237 | * 2 (Very Productive Time) 238 | * 239 | * @return integer 240 | */ 241 | public function getProductivityCode() 242 | { 243 | return $this->productivityCode; 244 | } 245 | 246 | /** 247 | * Return RescueTime activiy productivity grade description 248 | * 249 | * One of very distracting, distracting, neutral, productive, very productive 250 | * 251 | * @return string 252 | */ 253 | public function getProductivity() 254 | { 255 | switch ($this->productivityCode) { 256 | case -2: 257 | return "very distracting"; 258 | case -1: 259 | return "distracting"; 260 | case 0: 261 | return "neutral"; 262 | case 1: 263 | return "productive"; 264 | case 2: 265 | return "very productive"; 266 | } 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /src/RescueTime/Client.php: -------------------------------------------------------------------------------- 1 | 29 | * $Client = new \RescueTime\Client($apiKey); 30 | * $activities = $Client->getActivities("rank"); 31 | * 32 | * 33 | * @link https://www.rescuetime.com/analytic_api_setup/doc 34 | */ 35 | class Client 36 | { 37 | 38 | /** 39 | * RescueTime API token 40 | * 41 | * @var string 42 | */ 43 | private $apiKey; 44 | 45 | /** 46 | * Default HttpClient 47 | * 48 | * @var \RescueTime\HttpClient 49 | */ 50 | public $httpClient; 51 | 52 | /** 53 | * Constructs RescueTime client 54 | * 55 | * @param string $apiKey RescueTime API key generated in the Embed and Data API -> Setup Data API 56 | * @link https://www.rescuetime.com/anapi/setup 57 | */ 58 | public function __construct($apiKey) 59 | { 60 | $this->apiKey = $apiKey; 61 | $this->httpClient = new HttpClient($apiKey); 62 | } 63 | 64 | /** 65 | * Returns list of RescueTime activities for selected search criteria 66 | * 67 | * @param RequestQueryParameters A set of parameters to retrieve 68 | * 69 | * @return array<\RescueTime\Activity> All activities, or false if none found 70 | * 71 | * @throws \RuntimeException If API returns error 72 | * @see \RescueTime\RequestQueryParameters 73 | */ 74 | public function getActivities(RequestQueryParameters $requestQueryParameters) 75 | { 76 | $responseJsonArray = $this->httpClient->request($requestQueryParameters); 77 | 78 | if (array_key_exists('error', $responseJsonArray)) { 79 | throw new \RuntimeException("API returned error: " . $responseJsonArray['error']); 80 | } 81 | 82 | $result = array(); 83 | $rowHeaders = $responseJsonArray['row_headers']; 84 | foreach ($responseJsonArray['rows'] as $columns) { 85 | $result[] = new Activity($rowHeaders, $columns); 86 | } 87 | 88 | return $result ?: false; 89 | } 90 | 91 | /** 92 | * Returns list of daily summary activities 93 | * 94 | * 95 | * @return array<\RescueTime\DailyReport> All activities for previous two weeks without current day, or false if none found 96 | * 97 | * @throws \RuntimeException If API returns error 98 | * @see \RescueTime\RequestQueryParameters 99 | */ 100 | public function getDailySummary() 101 | { 102 | 103 | $responseJsonArray = $this->httpClient->requestDailyReport(); 104 | 105 | if (array_key_exists('error', $responseJsonArray)) { 106 | throw new \RuntimeException("API returned error: " . $responseJsonArray['error']); 107 | } 108 | 109 | $result = array(); 110 | foreach ($responseJsonArray as $responseRecord) { 111 | $result[] = new DailyReport($responseRecord); 112 | } 113 | 114 | return $result ?: false; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/RescueTime/DailyReport.php: -------------------------------------------------------------------------------- 1 | $value) { 442 | $this->$property = $value; 443 | } 444 | } 445 | 446 | /** 447 | * Returns activity date 448 | * 449 | * @return string 450 | */ 451 | public function getDate() 452 | { 453 | return $this->date; 454 | } 455 | 456 | /** 457 | * Returns overall productivity pulse. A scale bewteen 0-100 458 | * 459 | * 460 | * @return integer 461 | */ 462 | public function getProductivityPulse() 463 | { 464 | return $this->productivity_pulse; 465 | } 466 | 467 | /** 468 | * Returns percentage representation of time spent in very productivity category 469 | * 470 | * 471 | * @return float 472 | */ 473 | public function getVeryProductivePercentage() 474 | { 475 | return $this->very_productive_percentage; 476 | } 477 | 478 | /** 479 | * Returns percentage representation of time spent in productivity category 480 | * 481 | * 482 | * @return float 483 | */ 484 | public function getProductivePercentage() 485 | { 486 | return $this->productive_percentage; 487 | } 488 | 489 | /** 490 | * Returns percentage representation of time spent in neutral category 491 | * 492 | * 493 | * @return float 494 | */ 495 | public function getNeutralPercentage() 496 | { 497 | return $this->neutral_percentage; 498 | } 499 | 500 | /** 501 | * Returns percentage representation of time spent in distracting category 502 | * 503 | * 504 | * @return float 505 | */ 506 | public function getDistractingPercentage() 507 | { 508 | return $this->distracting_percentage; 509 | } 510 | 511 | /** 512 | * Returns percentage representation of time spent in all productive categories 513 | * 514 | * 515 | * @return float 516 | */ 517 | public function getAllProductivePercentage() 518 | { 519 | return $this->all_productive_percentage; 520 | } 521 | 522 | /** 523 | * Returns percentage representation of time spent in all distracting categories 524 | * 525 | * 526 | * @return float 527 | */ 528 | public function getAllDistractingPercentage() 529 | { 530 | return $this->all_distracting_percentage; 531 | } 532 | 533 | /** 534 | * Returns percentage representation of time spent in all uncategorized categories 535 | * 536 | * 537 | * @return float 538 | */ 539 | public function getUncategorizedPercentage() 540 | { 541 | return $this->uncategorized_percentage; 542 | } 543 | 544 | /** 545 | * Returns percentage representation of time spent in business category 546 | * 547 | * 548 | * @return float 549 | */ 550 | public function getBusinessPercentage() 551 | { 552 | return $this->business_percentage; 553 | } 554 | 555 | /** 556 | * Returns percentage representation of time spent in communication and scheduling category 557 | * 558 | * 559 | * @return float 560 | */ 561 | public function getCommunicationAndSchedulingPercentage() 562 | { 563 | return $this->communication_and_scheduling_percentage; 564 | } 565 | 566 | /** 567 | * Returns percentage representation of time spent in social networking category 568 | * 569 | * 570 | * @return float 571 | */ 572 | public function getSocialNetworkingPercentage() 573 | { 574 | return $this->social_networking_percentage; 575 | } 576 | 577 | /** 578 | * Returns percentage representation of time spent in design and composition categories 579 | * 580 | * 581 | * @return float 582 | */ 583 | public function getDesignAndCompositionPercentage() 584 | { 585 | return $this->design_and_composition_percentage; 586 | } 587 | 588 | /** 589 | * Returns percentage representation of time spent in entertainment category 590 | * 591 | * 592 | * @return float 593 | */ 594 | public function getEntertainmentPercentage() 595 | { 596 | return $this->entertainment_percentage; 597 | } 598 | 599 | /** 600 | * Returns percentage representation of time spent in news category 601 | * 602 | * 603 | * @return float 604 | */ 605 | public function getNewsPercentage() 606 | { 607 | return $this->news_percentage; 608 | } 609 | 610 | /** 611 | * Returns percentage representation of time spent in software development category 612 | * 613 | * 614 | * @return float 615 | */ 616 | public function getSoftwareDevelopmentPercentage() 617 | { 618 | return $this->software_development_percentage; 619 | } 620 | 621 | /** 622 | * Returns percentage representation of time spent in reference and learning categories 623 | * 624 | * 625 | * @return float 626 | */ 627 | public function getReferenceAndLearningPercentage() 628 | { 629 | return $this->reference_and_learning_percentage; 630 | } 631 | 632 | /** 633 | * Returns percentage representation of time spent in shopping category 634 | * 635 | * 636 | * @return float 637 | */ 638 | public function getShoppingPercentage() 639 | { 640 | return $this->shopping_percentage; 641 | } 642 | 643 | /** 644 | * Returns percentage representation of time spent in shopping category 645 | * 646 | * 647 | * @return float 648 | */ 649 | public function getUtilitiesPercentage() 650 | { 651 | return $this->utilities_percentage; 652 | } 653 | 654 | /** 655 | * Returns daily hours (Float with two decimal places) 656 | * 657 | * 658 | * @return float 659 | */ 660 | public function getTotalHours() 661 | { 662 | return $this->total_hours; 663 | } 664 | 665 | /** 666 | * Returns hours (Float with two decimal places) representation of time spent in very productivity category 667 | * 668 | * 669 | * @return float 670 | */ 671 | public function getVeryProductiveHours() 672 | { 673 | return $this->very_productive_hours; 674 | } 675 | 676 | /** 677 | * Returns hours (Float with two decimal places) representation of time spent in productivity category 678 | * 679 | * 680 | * @return float 681 | */ 682 | public function getProductiveHours() 683 | { 684 | return $this->productive_hours; 685 | } 686 | 687 | /** 688 | * Returns hours (Float with two decimal places) representation of time spent in neutral category 689 | * 690 | * 691 | * @return float 692 | */ 693 | public function getNeutralHours() 694 | { 695 | return $this->neutral_hours; 696 | } 697 | 698 | /** 699 | * Returns hours (Float with two decimal places) representation of time spent in distracting category 700 | * 701 | * 702 | * @return float 703 | */ 704 | public function getDistractingHours() 705 | { 706 | return $this->distracting_hours; 707 | } 708 | 709 | /** 710 | * Returns hours (Float with two decimal places) representation of time spent in distracting category 711 | * 712 | * 713 | * @return float 714 | */ 715 | public function getVeryDistractingHours() 716 | { 717 | return $this->very_distracting_hours; 718 | } 719 | 720 | /** 721 | * Returns hours (Float with two decimal places) representation of time spent in all productive categories 722 | * 723 | * 724 | * @return float 725 | */ 726 | public function getAllProductiveHours() 727 | { 728 | return $this->all_productive_hours; 729 | } 730 | 731 | /** 732 | * Returns hours (Float with two decimal places) representation of time spent in all distracting categories 733 | * 734 | * 735 | * @return float 736 | */ 737 | public function getAllDistractingHours() 738 | { 739 | return $this->all_distracting_hours; 740 | } 741 | 742 | /** 743 | * Returns hours (Float with two decimal places) representation of time spent in all uncategorized categories 744 | * 745 | * 746 | * @return float 747 | */ 748 | public function getUncategorizedHours() 749 | { 750 | return $this->uncategorized_hours; 751 | } 752 | 753 | /** 754 | * Returns hours (Float with two decimal places) representation of time spent in business category 755 | * 756 | * 757 | * @return float 758 | */ 759 | public function getBusinessHours() 760 | { 761 | return $this->business_hours; 762 | } 763 | 764 | /** 765 | * Returns hours (Float with two decimal places) representation of time spent in communication and scheduling category 766 | * 767 | * 768 | * @return float 769 | */ 770 | public function getCommunicationAndSchedulingHours() 771 | { 772 | return $this->communication_and_scheduling_hours; 773 | } 774 | 775 | /** 776 | * Returns hours (Float with two decimal places) representation of time spent in social networking category 777 | * 778 | * 779 | * @return float 780 | */ 781 | public function getSocialNetworkingHours() 782 | { 783 | return $this->social_networking_hours; 784 | } 785 | 786 | /** 787 | * Returns hours (Float with two decimal places) representation of time spent in design and composition categories 788 | * 789 | * 790 | * @return float 791 | */ 792 | public function getDesignAndCompositionHours() 793 | { 794 | return $this->design_and_composition_hours; 795 | } 796 | 797 | /** 798 | * Returns hours (Float with two decimal places) representation of time spent in entertainment category 799 | * 800 | * 801 | * @return float 802 | */ 803 | public function getEntertainmentHours() 804 | { 805 | return $this->entertainment_hours; 806 | } 807 | 808 | /** 809 | * Returns hours (Float with two decimal places) representation of time spent in news category 810 | * 811 | * 812 | * @return float 813 | */ 814 | public function getNewsHours() 815 | { 816 | return $this->news_hours; 817 | } 818 | 819 | /** 820 | * Returns hours (Float with two decimal places) representation of time spent in software development category 821 | * 822 | * 823 | * @return float 824 | */ 825 | public function getSoftwareDevelopmentHours() 826 | { 827 | return $this->software_development_hours; 828 | } 829 | 830 | /** 831 | * Returns hours (Float with two decimal places) representation of time spent in reference and learning categories 832 | * 833 | * 834 | * @return float 835 | */ 836 | public function getReferenceAndLearningHours() 837 | { 838 | return $this->reference_and_learning_hours; 839 | } 840 | 841 | /** 842 | * Returns hours (Float with two decimal places) representation of time spent in shopping category 843 | * 844 | * 845 | * @return float 846 | */ 847 | public function getShoppingHours() 848 | { 849 | return $this->shopping_hours; 850 | } 851 | 852 | /** 853 | * Returns hours (Float with two decimal places) representation of time spent in shopping category 854 | * 855 | * 856 | * @return float 857 | */ 858 | public function getUtilitiesHours() 859 | { 860 | return $this->utilities_hours; 861 | } 862 | 863 | /** 864 | * Returns daily hours (Float with two decimal places) 865 | * 866 | * 867 | * @return string 868 | */ 869 | public function getTotalDurationFormatted() 870 | { 871 | return $this->total_duration_formatted; 872 | } 873 | 874 | /** 875 | * Returns hours (Float with two decimal places) representation of time spent in very productivity category 876 | * 877 | * 878 | * @return string 879 | */ 880 | public function getVeryProductiveDurationFormatted() 881 | { 882 | return $this->very_productive_duration_formatted; 883 | } 884 | 885 | /** 886 | * Returns hours (Float with two decimal places) representation of time spent in productivity category 887 | * 888 | * 889 | * @return string 890 | */ 891 | public function getProductiveDurationFormatted() 892 | { 893 | return $this->productive_duration_formatted; 894 | } 895 | 896 | /** 897 | * Returns hours (Float with two decimal places) representation of time spent in neutral category 898 | * 899 | * 900 | * @return string 901 | */ 902 | public function getNeutralDurationFormatted() 903 | { 904 | return $this->neutral_duration_formatted; 905 | } 906 | 907 | /** 908 | * Returns hours (Float with two decimal places) representation of time spent in distracting category 909 | * 910 | * 911 | * @return string 912 | */ 913 | public function getDistractingDurationFormatted() 914 | { 915 | return $this->distracting_duration_formatted; 916 | } 917 | 918 | /** 919 | * Returns hours (Float with two decimal places) representation of time spent in distracting category 920 | * 921 | * 922 | * @return string 923 | */ 924 | public function getVeryDistractingDurationFormatted() 925 | { 926 | return $this->very_distracting_duration_formatted; 927 | } 928 | 929 | /** 930 | * Returns hours (Float with two decimal places) representation of time spent in all productive categories 931 | * 932 | * 933 | * @return string 934 | */ 935 | public function getAllProductiveDurationFormatted() 936 | { 937 | return $this->all_productive_duration_formatted; 938 | } 939 | 940 | /** 941 | * Returns hours (Float with two decimal places) representation of time spent in all distracting categories 942 | * 943 | * 944 | * @return string 945 | */ 946 | public function getAllDistractingDurationFormatted() 947 | { 948 | return $this->all_distracting_duration_formatted; 949 | } 950 | 951 | /** 952 | * Returns hours (Float with two decimal places) representation of time spent in all uncategorized categories 953 | * 954 | * 955 | * @return string 956 | */ 957 | public function getUncategorizedDurationFormatted() 958 | { 959 | return $this->uncategorized_duration_formatted; 960 | } 961 | 962 | /** 963 | * Returns hours (Float with two decimal places) representation of time spent in business category 964 | * 965 | * 966 | * @return string 967 | */ 968 | public function getBusinessDurationFormatted() 969 | { 970 | return $this->business_duration_formatted; 971 | } 972 | 973 | /** 974 | * Returns hours (Float with two decimal places) representation of time spent in communication and scheduling category 975 | * 976 | * 977 | * @return string 978 | */ 979 | public function getCommunicationAndSchedulingDurationFormatted() 980 | { 981 | return $this->communication_and_scheduling_duration_formatted; 982 | } 983 | 984 | /** 985 | * Returns hours (Float with two decimal places) representation of time spent in social networking category 986 | * 987 | * 988 | * @return string 989 | */ 990 | public function getSocialNetworkingDurationFormatted() 991 | { 992 | return $this->social_networking_duration_formatted; 993 | } 994 | 995 | /** 996 | * Returns hours (Float with two decimal places) representation of time spent in design and composition categories 997 | * 998 | * 999 | * @return string 1000 | */ 1001 | public function getDesignAndCompositionDurationFormatted() 1002 | { 1003 | return $this->design_and_composition_duration_formatted; 1004 | } 1005 | 1006 | /** 1007 | * Returns hours (Float with two decimal places) representation of time spent in entertainment category 1008 | * 1009 | * 1010 | * @return string 1011 | */ 1012 | public function getEntertainmentDurationFormatted() 1013 | { 1014 | return $this->entertainment_duration_formatted; 1015 | } 1016 | 1017 | /** 1018 | * Returns hours (Float with two decimal places) representation of time spent in news category 1019 | * 1020 | * 1021 | * @return string 1022 | */ 1023 | public function getNewsDurationFormatted() 1024 | { 1025 | return $this->news_duration_formatted; 1026 | } 1027 | 1028 | /** 1029 | * Returns hours (Float with two decimal places) representation of time spent in software development category 1030 | * 1031 | * 1032 | * @return string 1033 | */ 1034 | public function getSoftwareDevelopmentDurationFormatted() 1035 | { 1036 | return $this->software_development_duration_formatted; 1037 | } 1038 | 1039 | /** 1040 | * Returns hours (Float with two decimal places) representation of time spent in reference and learning categories 1041 | * 1042 | * 1043 | * @return string 1044 | */ 1045 | public function getReferenceAndLearningDurationFormatted() 1046 | { 1047 | return $this->reference_and_learning_duration_formatted; 1048 | } 1049 | 1050 | /** 1051 | * Returns hours (Float with two decimal places) representation of time spent in shopping category 1052 | * 1053 | * 1054 | * @return string 1055 | */ 1056 | public function getShoppingDurationFormatted() 1057 | { 1058 | return $this->shopping_duration_formatted; 1059 | } 1060 | 1061 | /** 1062 | * Returns hours (Float with two decimal places) representation of time spent in shopping category 1063 | * 1064 | * 1065 | * @return string 1066 | */ 1067 | public function getUtilitiesDurationFormatted() 1068 | { 1069 | return $this->utilities_duration_formatted; 1070 | } 1071 | } 1072 | -------------------------------------------------------------------------------- /src/RescueTime/HttpClient.php: -------------------------------------------------------------------------------- 1 | apiKey = $apiKey; 50 | $this->apiEndpoint = 'https://www.rescuetime.com'; 51 | $this->format = 'json'; 52 | $this->guzzleClient = null; 53 | } 54 | 55 | /** 56 | * Sends request to RescueTime API 57 | * 58 | * @return array Decoded json returned by server 59 | */ 60 | public function request(RequestQueryParameters $params) 61 | { 62 | $client = $this->guzzleClient ?: new GuzzleClient($this->apiEndpoint); 63 | 64 | $request = $client->get( 65 | '/anapi/data', 66 | ['Accept' => 'application/json'], 67 | ['query' => ['key' => $this->apiKey, 'format' => $this->format] + $params->toArray()] 68 | ); 69 | 70 | $response = $request->send(); 71 | 72 | return $this->handleResponse($response); 73 | } 74 | 75 | /** 76 | * Sends Daily Report request to RescueTime API 77 | * 78 | * @return array Decoded json returned by server 79 | */ 80 | public function requestDailyReport() 81 | { 82 | $client = $this->guzzleClient ?: new GuzzleClient($this->apiEndpoint); 83 | 84 | $request = $client->get( 85 | '/anapi/daily_summary_feed', 86 | ['Accept' => 'application/json'], 87 | ['query' => ['key' => $this->apiKey, 'format' => $this->format]] 88 | ); 89 | 90 | $response = $request->send(); 91 | 92 | return $this->handleResponse($response); 93 | } 94 | 95 | /** 96 | * Parses RescueTime API response and handles HTTP/API errors 97 | * @param String $response Raw API response 98 | * @return array Decoded json returned by server 99 | * @throws \Exception If API server can not be contacted or returns an error 100 | */ 101 | private function handleResponse($response) 102 | { 103 | if (!$response || !$response->isSuccessful()) { 104 | throw new \Exception("HTTP request failed"); 105 | } 106 | 107 | if ($response->getInfo("size_download") == 0) { 108 | throw new \Exception("HTTP body empty"); 109 | } 110 | 111 | try { 112 | $responseJsonArray = json_decode($response->getBody(), true); 113 | } catch (RuntimeException $e) { 114 | throw new \Exception("Invalid JSON response: " . $e->getMessage()); 115 | } 116 | 117 | return $responseJsonArray; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/RescueTime/RequestQueryParameters.php: -------------------------------------------------------------------------------- 1 | null, 181 | 'version' => null, 182 | 'perspective' => 'rank', 183 | 'resolution_time' => 'hour', 184 | 'restrict_group' => null, 185 | 'restrict_user' => null, 186 | 'restrict_begin' => null, 187 | 'restrict_end' => null, 188 | 'restrict_kind' => null, 189 | 'restrict_project' => null, 190 | 'restrict_thing' => null, 191 | 'restrict_thingy' => null, 192 | ]; 193 | 194 | /** 195 | * Constructs RequestQueryParameters class 196 | * 197 | * Available options to specify in the params argument: 198 | * 199 | * string perspective One of "rank", "interval", "member" 200 | * string resolution_time One of "month", "week", "day", "hour" 201 | * string restrict_group One group name 202 | * string restrict_user One user name or user email 203 | * \DateTime restrict_begin Sets the start day for data batch 204 | * \DateTime restrict_end Sets the end day for data batch 205 | * string restrict_kind One of "category", "activity", "productivity", "document" 206 | * string restrict_project Name of project 207 | * string restrict_thing Name of category, activity, or overview 208 | * string restrict_thingy Name of specific "document" or "activity" 209 | * 210 | * @param array $params 211 | */ 212 | public function __construct(array $params) 213 | { 214 | $params = $params + $this->defaultParams; 215 | $this->operation = 'select'; 216 | $this->version = 0; 217 | $this->setPerspective($params['perspective']); 218 | $this->setResolutionTime($params['resolution_time']); 219 | $this->restrict_group = $params['restrict_group']; 220 | $this->restrict_user = $params['restrict_user']; 221 | $this->restrict_begin = $params['restrict_begin']; 222 | $this->restrict_end = $params['restrict_end']; 223 | $this->setRestrictKind($params['restrict_kind']); 224 | $this->restrict_project = $params['restrict_project']; 225 | $this->restrict_thing = $params['restrict_thing']; 226 | $this->restrict_thingy = $params['restrict_thingy']; 227 | } 228 | 229 | /** 230 | * Class properties accessor 231 | * 232 | * Utilized for reading data from inaccessible properties 233 | * 234 | * @param string $attribute Property name 235 | * 236 | * @return mixed Property value 237 | */ 238 | public function __get($attribute) 239 | { 240 | if (property_exists($this, $attribute)) { 241 | return $this->$attribute; 242 | } 243 | 244 | return null; 245 | } 246 | 247 | /** 248 | * Sets perspective property 249 | * Please refer to @availablePerspectives for available options 250 | * 251 | * @param string $perspective 252 | * @throws \InvalidArgumentException If perspective not in available perspectives 253 | */ 254 | public function setPerspective($perspective) 255 | { 256 | if ($perspective && !in_array($perspective, $this->availablePerspectives)) { 257 | throw new \InvalidArgumentException( 258 | sprintf("Perspective must be one of %s", implode(', ', $this->availablePerspectives)) 259 | ); 260 | } 261 | $this->perspective = $perspective; 262 | } 263 | 264 | /** 265 | * Sets resolution property 266 | * Please refer to @availableResolutionTimes for available options 267 | * 268 | * @param string $resolution 269 | * @throws \InvalidArgumentException If resolution not in available resolution times 270 | */ 271 | public function setResolutionTime($resolution) 272 | { 273 | if ($resolution && !in_array($resolution, $this->availableResolutionTimes)) { 274 | throw new \InvalidArgumentException( 275 | sprintf("Resolution time must be one of %s", implode(', ', $this->availableResolutionTimes)) 276 | ); 277 | } 278 | $this->resolution_time = $resolution; 279 | } 280 | 281 | /** 282 | * Sets resetrict_kind property 283 | * Please refer to @availableRestrictKinds for available options 284 | * 285 | * @param string $kind 286 | * @throws \InvalidArgumentException If property not in available restrict kinds 287 | */ 288 | public function setRestrictKind($kind) 289 | { 290 | if ($kind && !in_array($kind, $this->availableRestrictKinds)) { 291 | throw new \InvalidArgumentException( 292 | sprintf("Restrict kind must be one of %s", implode(', ', $this->availableRestrictKinds)) 293 | ); 294 | } 295 | $this->restrict_kind = $kind; 296 | } 297 | 298 | /** 299 | * Serializes query parameters array to API endpoint specific format 300 | * 301 | * @return array 302 | */ 303 | public function toArray() 304 | { 305 | $queryParams = array( 306 | 'operation' => $this->operation, 307 | 'version' => $this->version, 308 | 'perspective' => $this->perspective, 309 | 'resolution_time' => $this->resolution_time, 310 | 'restrict_group' => $this->restrict_group, 311 | 'restrict_user' => $this->restrict_user, 312 | 'restrict_kind' => $this->restrict_kind, 313 | 'restrict_project' => $this->restrict_project, 314 | 'restrict_thing' => $this->restrict_thing, 315 | 'restrict_thingy' => $this->restrict_thingy 316 | ); 317 | if ($this->restrict_begin) { 318 | $queryParams['restrict_begin'] = $this->restrict_begin->format('Y-m-d'); 319 | } 320 | if ($this->restrict_end) { 321 | $queryParams['restrict_end'] = $this->restrict_end->format('Y-m-d'); 322 | } 323 | 324 | return array_filter($queryParams); 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /tests/RescueTime/Tests/ClientTest.php: -------------------------------------------------------------------------------- 1 | Client = new \RescueTime\Client($apiKey); 18 | } 19 | 20 | /** 21 | * tearDown method 22 | * 23 | * @return void 24 | */ 25 | public function tearDown() 26 | { 27 | parent::tearDown(); 28 | unset($this->Client); 29 | } 30 | 31 | public function testGetByRank() 32 | { 33 | $data = file_get_contents(__DIR__ . '/Fakes/rank.json'); 34 | 35 | $requestParams = array('perspective' => 'rank'); 36 | 37 | $httpClient = $this->getMockBuilder('\RescueTime\HttpClient') 38 | ->setConstructorArgs(array($requestParams)) 39 | ->setMethods(array('request')) 40 | ->getMock(); 41 | $httpClient->expects($this->once()) 42 | ->method('request') 43 | ->will($this->returnValue(json_decode($data, true))); 44 | 45 | $this->Client->httpClient = $httpClient; 46 | 47 | $activities = $this->Client->getActivities(new Params($requestParams)); 48 | 49 | $this->assertTrue(is_array($activities), "Expected activities to be an array"); 50 | $this->assertEquals(15, count($activities), "Expected to return 15 activities"); 51 | } 52 | 53 | public function testGetByInterval() 54 | { 55 | $data = file_get_contents(__DIR__ . '/Fakes/interval.json'); 56 | 57 | $requestParams = array('perspective' => 'interval'); 58 | 59 | $httpClient = $this->getMockBuilder('\RescueTime\HttpClient') 60 | ->setConstructorArgs(array($requestParams)) 61 | ->setMethods(array('request')) 62 | ->getMock(); 63 | $httpClient->expects($this->once()) 64 | ->method('request') 65 | ->will($this->returnValue(json_decode($data, true))); 66 | 67 | $this->Client->httpClient = $httpClient; 68 | 69 | $activities = $this->Client->getActivities(new Params($requestParams)); 70 | 71 | $this->assertTrue(is_array($activities), "Expected activities to be an array"); 72 | $this->assertEquals(14, count($activities), "Expected to return 14 activities"); 73 | } 74 | 75 | public function testGetByMember() 76 | { 77 | $data = file_get_contents(__DIR__ . '/Fakes/member.json'); 78 | 79 | $requestParams = array('perspective' => 'member'); 80 | 81 | $httpClient = $this->getMockBuilder('\RescueTime\HttpClient') 82 | ->setConstructorArgs(array($requestParams)) 83 | ->setMethods(array('request')) 84 | ->getMock(); 85 | $httpClient->expects($this->once()) 86 | ->method('request') 87 | ->will($this->returnValue(json_decode($data, true))); 88 | 89 | $this->Client->httpClient = $httpClient; 90 | 91 | $activities = $this->Client->getActivities(new Params($requestParams)); 92 | 93 | $this->assertTrue(is_array($activities), "Expected activities to be an array"); 94 | $this->assertEquals(17, count($activities), "Expected to return 17 activities"); 95 | } 96 | 97 | public function testGetDailyReport() 98 | { 99 | $data = file_get_contents(__DIR__ . '/Fakes/daily_report.json'); 100 | $requestParams = array(); 101 | 102 | $httpClient = $this->getMockBuilder('\RescueTime\HttpClient') 103 | ->setConstructorArgs(array($requestParams)) 104 | ->setMethods(array('requestDailyReport')) 105 | ->getMock(); 106 | $httpClient->expects($this->once()) 107 | ->method('requestDailyReport') 108 | ->will($this->returnValue(json_decode($data, true))); 109 | 110 | $this->Client->httpClient = $httpClient; 111 | 112 | $report = $this->Client->getDailySummary(); 113 | 114 | $this->assertTrue(is_array($report), "Expected activities to be an array"); 115 | $this->assertEquals(15, count($report), "Expected to return 15 activities"); 116 | } 117 | 118 | /** 119 | * @expectedException InvalidArgumentException 120 | * @expectedExceptionMessage Perspective must be one of rank, interval, member 121 | */ 122 | public function testInvalidPerspective() 123 | { 124 | $this->Client->getActivities(new Params(['perspective' => 'invalid_perspective_name'])); 125 | } 126 | 127 | /** 128 | * @expectedException InvalidArgumentException 129 | * @expectedExceptionMessage Resolution time must be one of month, week, day, hour 130 | */ 131 | public function testInvalidResolutionTime() 132 | { 133 | $this->Client->getActivities(new Params(['resolution_time' => 'invalid_resolution_time'])); 134 | } 135 | 136 | /** 137 | * @expectedException InvalidArgumentException 138 | * @expectedExceptionMessage Restrict kind must be one of category, activity, productivity 139 | */ 140 | public function testInvalidRestrictKind() 141 | { 142 | $this->Client->getActivities(new Params(['restrict_kind' => 'invalid_restrict_kind'])); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /tests/RescueTime/Tests/Fakes/daily_report.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id":1465369200, 4 | "date":"2016-06-08", 5 | "productivity_pulse":65, 6 | "very_productive_percentage":46.8, 7 | "productive_percentage":5.9, 8 | "neutral_percentage":22.6, 9 | "distracting_percentage":10.5, 10 | "very_distracting_percentage":14.1, 11 | "all_productive_percentage":52.7, 12 | "all_distracting_percentage":24.6, 13 | "uncategorized_percentage":7.7, 14 | "business_percentage":5.2, 15 | "communication_and_scheduling_percentage":14.6, 16 | "social_networking_percentage":11.5, 17 | "design_and_composition_percentage":7.6, 18 | "entertainment_percentage":1, 19 | "news_percentage":2.4, 20 | "software_development_percentage":34, 21 | "reference_and_learning_percentage":3.6, 22 | "shopping_percentage":0.1, 23 | "utilities_percentage":12.4, 24 | "total_hours":12.22, 25 | "very_productive_hours":5.72, 26 | "productive_hours":0.73, 27 | "neutral_hours":2.76, 28 | "distracting_hours":1.28, 29 | "very_distracting_hours":1.73, 30 | "all_productive_hours":6.44, 31 | "all_distracting_hours":3.01, 32 | "uncategorized_hours":0.94, 33 | "business_hours":0.64, 34 | "communication_and_scheduling_hours":1.78, 35 | "social_networking_hours":1.4, 36 | "design_and_composition_hours":0.92, 37 | "entertainment_hours":0.13, 38 | "news_hours":0.29, 39 | "software_development_hours":4.15, 40 | "reference_and_learning_hours":0.44, 41 | "shopping_hours":0.01, 42 | "utilities_hours":1.51, 43 | "total_duration_formatted":"12h 12m", 44 | "very_productive_duration_formatted":"5h 43m", 45 | "productive_duration_formatted":"43m 30s", 46 | "neutral_duration_formatted":"2h 45m", 47 | "distracting_duration_formatted":"1h 17m", 48 | "very_distracting_duration_formatted":"1h 43m", 49 | "all_productive_duration_formatted":"6h 26m", 50 | "all_distracting_duration_formatted":"3h", 51 | "uncategorized_duration_formatted":"56m 7s", 52 | "business_duration_formatted":"38m 28s", 53 | "communication_and_scheduling_duration_formatted":"1h 46m", 54 | "social_networking_duration_formatted":"1h 24m", 55 | "design_and_composition_duration_formatted":"55m 22s", 56 | "entertainment_duration_formatted":"7m 32s", 57 | "news_duration_formatted":"17m 29s", 58 | "software_development_duration_formatted":"4h 9m", 59 | "reference_and_learning_duration_formatted":"26m 39s", 60 | "shopping_duration_formatted":"41s", 61 | "utilities_duration_formatted":"1h 30m" 62 | }, 63 | { 64 | "id":1465282800, 65 | "date":"2016-06-07", 66 | "productivity_pulse":65, 67 | "very_productive_percentage":48.3, 68 | "productive_percentage":5.1, 69 | "neutral_percentage":18.2, 70 | "distracting_percentage":16.4, 71 | "very_distracting_percentage":12, 72 | "all_productive_percentage":53.4, 73 | "all_distracting_percentage":28.4, 74 | "uncategorized_percentage":11.9, 75 | "business_percentage":5.6, 76 | "communication_and_scheduling_percentage":16.4, 77 | "social_networking_percentage":7.9, 78 | "design_and_composition_percentage":0.7, 79 | "entertainment_percentage":1.6, 80 | "news_percentage":5.2, 81 | "software_development_percentage":42, 82 | "reference_and_learning_percentage":3.9, 83 | "shopping_percentage":0.3, 84 | "utilities_percentage":4.4, 85 | "total_hours":10.56, 86 | "very_productive_hours":5.1, 87 | "productive_hours":0.54, 88 | "neutral_hours":1.93, 89 | "distracting_hours":1.73, 90 | "very_distracting_hours":1.27, 91 | "all_productive_hours":5.64, 92 | "all_distracting_hours":3, 93 | "uncategorized_hours":1.26, 94 | "business_hours":0.59, 95 | "communication_and_scheduling_hours":1.73, 96 | "social_networking_hours":0.84, 97 | "design_and_composition_hours":0.07, 98 | "entertainment_hours":0.17, 99 | "news_hours":0.55, 100 | "software_development_hours":4.43, 101 | "reference_and_learning_hours":0.41, 102 | "shopping_hours":0.03, 103 | "utilities_hours":0.47, 104 | "total_duration_formatted":"10h 33m", 105 | "very_productive_duration_formatted":"5h 5m", 106 | "productive_duration_formatted":"32m 14s", 107 | "neutral_duration_formatted":"1h 55m", 108 | "distracting_duration_formatted":"1h 43m", 109 | "very_distracting_duration_formatted":"1h 16m", 110 | "all_productive_duration_formatted":"5h 38m", 111 | "all_distracting_duration_formatted":"2h 59m", 112 | "uncategorized_duration_formatted":"1h 15m", 113 | "business_duration_formatted":"35m 38s", 114 | "communication_and_scheduling_duration_formatted":"1h 43m", 115 | "social_networking_duration_formatted":"50m 18s", 116 | "design_and_composition_duration_formatted":"4m 18s", 117 | "entertainment_duration_formatted":"10m 12s", 118 | "news_duration_formatted":"33m 9s", 119 | "software_development_duration_formatted":"4h 26m", 120 | "reference_and_learning_duration_formatted":"24m 48s", 121 | "shopping_duration_formatted":"1m 50s", 122 | "utilities_duration_formatted":"27m 55s" 123 | }, 124 | { 125 | "id":1465196400, 126 | "date":"2016-06-06", 127 | "productivity_pulse":64, 128 | "very_productive_percentage":48.6, 129 | "productive_percentage":6.7, 130 | "neutral_percentage":13.9, 131 | "distracting_percentage":16, 132 | "very_distracting_percentage":14.8, 133 | "all_productive_percentage":55.3, 134 | "all_distracting_percentage":30.7, 135 | "uncategorized_percentage":6.9, 136 | "business_percentage":15, 137 | "communication_and_scheduling_percentage":20.6, 138 | "social_networking_percentage":10.4, 139 | "design_and_composition_percentage":1.6, 140 | "entertainment_percentage":1.3, 141 | "news_percentage":4.1, 142 | "software_development_percentage":32, 143 | "reference_and_learning_percentage":3.1, 144 | "shopping_percentage":0, 145 | "utilities_percentage":4.9, 146 | "total_hours":11.53, 147 | "very_productive_hours":5.61, 148 | "productive_hours":0.77, 149 | "neutral_hours":1.61, 150 | "distracting_hours":1.84, 151 | "very_distracting_hours":1.7, 152 | "all_productive_hours":6.38, 153 | "all_distracting_hours":3.55, 154 | "uncategorized_hours":0.8, 155 | "business_hours":1.73, 156 | "communication_and_scheduling_hours":2.37, 157 | "social_networking_hours":1.2, 158 | "design_and_composition_hours":0.18, 159 | "entertainment_hours":0.15, 160 | "news_hours":0.48, 161 | "software_development_hours":3.69, 162 | "reference_and_learning_hours":0.35, 163 | "shopping_hours":0, 164 | "utilities_hours":0.56, 165 | "total_duration_formatted":"11h 32m", 166 | "very_productive_duration_formatted":"5h 36m", 167 | "productive_duration_formatted":"46m 23s", 168 | "neutral_duration_formatted":"1h 36m", 169 | "distracting_duration_formatted":"1h 50m", 170 | "very_distracting_duration_formatted":"1h 42m", 171 | "all_productive_duration_formatted":"6h 22m", 172 | "all_distracting_duration_formatted":"3h 32m", 173 | "uncategorized_duration_formatted":"48m 3s", 174 | "business_duration_formatted":"1h 43m", 175 | "communication_and_scheduling_duration_formatted":"2h 22m", 176 | "social_networking_duration_formatted":"1h 12m", 177 | "design_and_composition_duration_formatted":"11m 4s", 178 | "entertainment_duration_formatted":"9m 16s", 179 | "news_duration_formatted":"28m 39s", 180 | "software_development_duration_formatted":"3h 41m", 181 | "reference_and_learning_duration_formatted":"21m 11s", 182 | "shopping_duration_formatted":"10s", 183 | "utilities_duration_formatted":"33m 37s" 184 | }, 185 | { 186 | "id":1465110000, 187 | "date":"2016-06-05", 188 | "productivity_pulse":39, 189 | "very_productive_percentage":21.6, 190 | "productive_percentage":10.1, 191 | "neutral_percentage":17.7, 192 | "distracting_percentage":6.9, 193 | "very_distracting_percentage":43.8, 194 | "all_productive_percentage":31.7, 195 | "all_distracting_percentage":50.6, 196 | "uncategorized_percentage":13.4, 197 | "business_percentage":0.3, 198 | "communication_and_scheduling_percentage":4.6, 199 | "social_networking_percentage":27.8, 200 | "design_and_composition_percentage":12.3, 201 | "entertainment_percentage":6.5, 202 | "news_percentage":11.2, 203 | "software_development_percentage":8.9, 204 | "reference_and_learning_percentage":7.9, 205 | "shopping_percentage":0.1, 206 | "utilities_percentage":6.9, 207 | "total_hours":6.36, 208 | "very_productive_hours":1.37, 209 | "productive_hours":0.64, 210 | "neutral_hours":1.13, 211 | "distracting_hours":0.44, 212 | "very_distracting_hours":2.79, 213 | "all_productive_hours":2.02, 214 | "all_distracting_hours":3.22, 215 | "uncategorized_hours":0.85, 216 | "business_hours":0.02, 217 | "communication_and_scheduling_hours":0.29, 218 | "social_networking_hours":1.77, 219 | "design_and_composition_hours":0.78, 220 | "entertainment_hours":0.41, 221 | "news_hours":0.71, 222 | "software_development_hours":0.57, 223 | "reference_and_learning_hours":0.5, 224 | "shopping_hours":0.01, 225 | "utilities_hours":0.44, 226 | "total_duration_formatted":"6h 21m", 227 | "very_productive_duration_formatted":"1h 22m", 228 | "productive_duration_formatted":"38m 33s", 229 | "neutral_duration_formatted":"1h 7m", 230 | "distracting_duration_formatted":"26m 14s", 231 | "very_distracting_duration_formatted":"2h 47m", 232 | "all_productive_duration_formatted":"2h", 233 | "all_distracting_duration_formatted":"3h 13m", 234 | "uncategorized_duration_formatted":"51m 6s", 235 | "business_duration_formatted":"1m 15s", 236 | "communication_and_scheduling_duration_formatted":"17m 27s", 237 | "social_networking_duration_formatted":"1h 46m", 238 | "design_and_composition_duration_formatted":"47m", 239 | "entertainment_duration_formatted":"24m 53s", 240 | "news_duration_formatted":"42m 42s", 241 | "software_development_duration_formatted":"34m 6s", 242 | "reference_and_learning_duration_formatted":"30m 17s", 243 | "shopping_duration_formatted":"20s", 244 | "utilities_duration_formatted":"26m 27s" 245 | }, 246 | { 247 | "id":1465023600, 248 | "date":"2016-06-04", 249 | "productivity_pulse":42, 250 | "very_productive_percentage":25.7, 251 | "productive_percentage":11.1, 252 | "neutral_percentage":14.9, 253 | "distracting_percentage":2.2, 254 | "very_distracting_percentage":46, 255 | "all_productive_percentage":36.9, 256 | "all_distracting_percentage":48.2, 257 | "uncategorized_percentage":9.6, 258 | "business_percentage":0.9, 259 | "communication_and_scheduling_percentage":4.7, 260 | "social_networking_percentage":32.7, 261 | "design_and_composition_percentage":22.7, 262 | "entertainment_percentage":5.6, 263 | "news_percentage":4.8, 264 | "software_development_percentage":2.1, 265 | "reference_and_learning_percentage":4.8, 266 | "shopping_percentage":0.9, 267 | "utilities_percentage":11.1, 268 | "total_hours":7.02, 269 | "very_productive_hours":1.81, 270 | "productive_hours":0.78, 271 | "neutral_hours":1.05, 272 | "distracting_hours":0.15, 273 | "very_distracting_hours":3.23, 274 | "all_productive_hours":2.59, 275 | "all_distracting_hours":3.38, 276 | "uncategorized_hours":0.68, 277 | "business_hours":0.06, 278 | "communication_and_scheduling_hours":0.33, 279 | "social_networking_hours":2.3, 280 | "design_and_composition_hours":1.6, 281 | "entertainment_hours":0.4, 282 | "news_hours":0.34, 283 | "software_development_hours":0.15, 284 | "reference_and_learning_hours":0.34, 285 | "shopping_hours":0.06, 286 | "utilities_hours":0.78, 287 | "total_duration_formatted":"7h 1m", 288 | "very_productive_duration_formatted":"1h 48m", 289 | "productive_duration_formatted":"46m 58s", 290 | "neutral_duration_formatted":"1h 2m", 291 | "distracting_duration_formatted":"9m 9s", 292 | "very_distracting_duration_formatted":"3h 13m", 293 | "all_productive_duration_formatted":"2h 35m", 294 | "all_distracting_duration_formatted":"3h 23m", 295 | "uncategorized_duration_formatted":"40m 31s", 296 | "business_duration_formatted":"3m 42s", 297 | "communication_and_scheduling_duration_formatted":"19m 50s", 298 | "social_networking_duration_formatted":"2h 17m", 299 | "design_and_composition_duration_formatted":"1h 35m", 300 | "entertainment_duration_formatted":"23m 44s", 301 | "news_duration_formatted":"20m 15s", 302 | "software_development_duration_formatted":"8m 55s", 303 | "reference_and_learning_duration_formatted":"20m 22s", 304 | "shopping_duration_formatted":"3m 37s", 305 | "utilities_duration_formatted":"46m 50s" 306 | }, 307 | { 308 | "id":1464937200, 309 | "date":"2016-06-03", 310 | "productivity_pulse":57, 311 | "very_productive_percentage":37.2, 312 | "productive_percentage":11.9, 313 | "neutral_percentage":10.2, 314 | "distracting_percentage":23.2, 315 | "very_distracting_percentage":17.4, 316 | "all_productive_percentage":49.1, 317 | "all_distracting_percentage":40.7, 318 | "uncategorized_percentage":4.1, 319 | "business_percentage":15.7, 320 | "communication_and_scheduling_percentage":24, 321 | "social_networking_percentage":16, 322 | "design_and_composition_percentage":4.7, 323 | "entertainment_percentage":0.7, 324 | "news_percentage":3.7, 325 | "software_development_percentage":16.8, 326 | "reference_and_learning_percentage":9.8, 327 | "shopping_percentage":0, 328 | "utilities_percentage":4.4, 329 | "total_hours":10.12, 330 | "very_productive_hours":3.77, 331 | "productive_hours":1.2, 332 | "neutral_hours":1.03, 333 | "distracting_hours":2.35, 334 | "very_distracting_hours":1.77, 335 | "all_productive_hours":4.97, 336 | "all_distracting_hours":4.12, 337 | "uncategorized_hours":0.42, 338 | "business_hours":1.59, 339 | "communication_and_scheduling_hours":2.43, 340 | "social_networking_hours":1.62, 341 | "design_and_composition_hours":0.47, 342 | "entertainment_hours":0.07, 343 | "news_hours":0.38, 344 | "software_development_hours":1.7, 345 | "reference_and_learning_hours":0.99, 346 | "shopping_hours":0, 347 | "utilities_hours":0.44, 348 | "total_duration_formatted":"10h 7m", 349 | "very_productive_duration_formatted":"3h 46m", 350 | "productive_duration_formatted":"1h 12m", 351 | "neutral_duration_formatted":"1h 1m", 352 | "distracting_duration_formatted":"2h 21m", 353 | "very_distracting_duration_formatted":"1h 45m", 354 | "all_productive_duration_formatted":"4h 58m", 355 | "all_distracting_duration_formatted":"4h 6m", 356 | "uncategorized_duration_formatted":"24m 59s", 357 | "business_duration_formatted":"1h 35m", 358 | "communication_and_scheduling_duration_formatted":"2h 25m", 359 | "social_networking_duration_formatted":"1h 37m", 360 | "design_and_composition_duration_formatted":"28m 16s", 361 | "entertainment_duration_formatted":"4m 20s", 362 | "news_duration_formatted":"22m 45s", 363 | "software_development_duration_formatted":"1h 42m", 364 | "reference_and_learning_duration_formatted":"59m 19s", 365 | "shopping_duration_formatted":"11s", 366 | "utilities_duration_formatted":"26m 30s" 367 | }, 368 | { 369 | "id":1464850800, 370 | "date":"2016-06-02", 371 | "productivity_pulse":48, 372 | "very_productive_percentage":25.7, 373 | "productive_percentage":7.6, 374 | "neutral_percentage":25.3, 375 | "distracting_percentage":18.7, 376 | "very_distracting_percentage":22.7, 377 | "all_productive_percentage":33.3, 378 | "all_distracting_percentage":41.5, 379 | "uncategorized_percentage":13, 380 | "business_percentage":10.8, 381 | "communication_and_scheduling_percentage":24.7, 382 | "social_networking_percentage":17.2, 383 | "design_and_composition_percentage":2.4, 384 | "entertainment_percentage":3.3, 385 | "news_percentage":2.1, 386 | "software_development_percentage":12.5, 387 | "reference_and_learning_percentage":4.6, 388 | "shopping_percentage":0.4, 389 | "utilities_percentage":9, 390 | "total_hours":11.38, 391 | "very_productive_hours":2.92, 392 | "productive_hours":0.87, 393 | "neutral_hours":2.88, 394 | "distracting_hours":2.13, 395 | "very_distracting_hours":2.59, 396 | "all_productive_hours":3.78, 397 | "all_distracting_hours":4.72, 398 | "uncategorized_hours":1.48, 399 | "business_hours":1.23, 400 | "communication_and_scheduling_hours":2.82, 401 | "social_networking_hours":1.96, 402 | "design_and_composition_hours":0.27, 403 | "entertainment_hours":0.38, 404 | "news_hours":0.24, 405 | "software_development_hours":1.42, 406 | "reference_and_learning_hours":0.52, 407 | "shopping_hours":0.05, 408 | "utilities_hours":1.02, 409 | "total_duration_formatted":"11h 22m", 410 | "very_productive_duration_formatted":"2h 55m", 411 | "productive_duration_formatted":"51m 56s", 412 | "neutral_duration_formatted":"2h 52m", 413 | "distracting_duration_formatted":"2h 8m", 414 | "very_distracting_duration_formatted":"2h 35m", 415 | "all_productive_duration_formatted":"3h 47m", 416 | "all_distracting_duration_formatted":"4h 43m", 417 | "uncategorized_duration_formatted":"1h 28m", 418 | "business_duration_formatted":"1h 13m", 419 | "communication_and_scheduling_duration_formatted":"2h 48m", 420 | "social_networking_duration_formatted":"1h 57m", 421 | "design_and_composition_duration_formatted":"16m 14s", 422 | "entertainment_duration_formatted":"22m 33s", 423 | "news_duration_formatted":"14m 34s", 424 | "software_development_duration_formatted":"1h 25m", 425 | "reference_and_learning_duration_formatted":"31m 11s", 426 | "shopping_duration_formatted":"2m 57s", 427 | "utilities_duration_formatted":"1h 1m" 428 | }, 429 | { 430 | "id":1464764400, 431 | "date":"2016-06-01", 432 | "productivity_pulse":55, 433 | "very_productive_percentage":36.4, 434 | "productive_percentage":9.7, 435 | "neutral_percentage":13.6, 436 | "distracting_percentage":22, 437 | "very_distracting_percentage":18.3, 438 | "all_productive_percentage":46.1, 439 | "all_distracting_percentage":40.3, 440 | "uncategorized_percentage":5.8, 441 | "business_percentage":18.2, 442 | "communication_and_scheduling_percentage":24, 443 | "social_networking_percentage":14, 444 | "design_and_composition_percentage":10.9, 445 | "entertainment_percentage":2.5, 446 | "news_percentage":4.7, 447 | "software_development_percentage":7.2, 448 | "reference_and_learning_percentage":4.3, 449 | "shopping_percentage":0, 450 | "utilities_percentage":8.3, 451 | "total_hours":11.12, 452 | "very_productive_hours":4.05, 453 | "productive_hours":1.08, 454 | "neutral_hours":1.51, 455 | "distracting_hours":2.45, 456 | "very_distracting_hours":2.03, 457 | "all_productive_hours":5.12, 458 | "all_distracting_hours":4.48, 459 | "uncategorized_hours":0.65, 460 | "business_hours":2.03, 461 | "communication_and_scheduling_hours":2.67, 462 | "social_networking_hours":1.56, 463 | "design_and_composition_hours":1.22, 464 | "entertainment_hours":0.28, 465 | "news_hours":0.52, 466 | "software_development_hours":0.8, 467 | "reference_and_learning_hours":0.48, 468 | "shopping_hours":0, 469 | "utilities_hours":0.92, 470 | "total_duration_formatted":"11h 7m", 471 | "very_productive_duration_formatted":"4h 2m", 472 | "productive_duration_formatted":"1h 4m", 473 | "neutral_duration_formatted":"1h 30m", 474 | "distracting_duration_formatted":"2h 26m", 475 | "very_distracting_duration_formatted":"2h 1m", 476 | "all_productive_duration_formatted":"5h 7m", 477 | "all_distracting_duration_formatted":"4h 28m", 478 | "uncategorized_duration_formatted":"38m 50s", 479 | "business_duration_formatted":"2h 1m", 480 | "communication_and_scheduling_duration_formatted":"2h 39m", 481 | "social_networking_duration_formatted":"1h 33m", 482 | "design_and_composition_duration_formatted":"1h 13m", 483 | "entertainment_duration_formatted":"16m 51s", 484 | "news_duration_formatted":"31m 19s", 485 | "software_development_duration_formatted":"48m 11s", 486 | "reference_and_learning_duration_formatted":"28m 35s", 487 | "shopping_duration_formatted":"no time", 488 | "utilities_duration_formatted":"55m 22s" 489 | }, 490 | { 491 | "id":1464678000, 492 | "date":"2016-05-31", 493 | "productivity_pulse":46, 494 | "very_productive_percentage":25.2, 495 | "productive_percentage":7.3, 496 | "neutral_percentage":17, 497 | "distracting_percentage":31.2, 498 | "very_distracting_percentage":19.4, 499 | "all_productive_percentage":32.5, 500 | "all_distracting_percentage":50.6, 501 | "uncategorized_percentage":7.5, 502 | "business_percentage":15.1, 503 | "communication_and_scheduling_percentage":35.7, 504 | "social_networking_percentage":15.5, 505 | "design_and_composition_percentage":4.6, 506 | "entertainment_percentage":1.2, 507 | "news_percentage":4.6, 508 | "software_development_percentage":5.5, 509 | "reference_and_learning_percentage":5.5, 510 | "shopping_percentage":0.2, 511 | "utilities_percentage":4.6, 512 | "total_hours":10.01, 513 | "very_productive_hours":2.52, 514 | "productive_hours":0.73, 515 | "neutral_hours":1.7, 516 | "distracting_hours":3.12, 517 | "very_distracting_hours":1.94, 518 | "all_productive_hours":3.25, 519 | "all_distracting_hours":5.06, 520 | "uncategorized_hours":0.75, 521 | "business_hours":1.51, 522 | "communication_and_scheduling_hours":3.57, 523 | "social_networking_hours":1.55, 524 | "design_and_composition_hours":0.46, 525 | "entertainment_hours":0.12, 526 | "news_hours":0.46, 527 | "software_development_hours":0.55, 528 | "reference_and_learning_hours":0.55, 529 | "shopping_hours":0.02, 530 | "utilities_hours":0.46, 531 | "total_duration_formatted":"10h", 532 | "very_productive_duration_formatted":"2h 31m", 533 | "productive_duration_formatted":"43m 45s", 534 | "neutral_duration_formatted":"1h 41m", 535 | "distracting_duration_formatted":"3h 7m", 536 | "very_distracting_duration_formatted":"1h 56m", 537 | "all_productive_duration_formatted":"3h 15m", 538 | "all_distracting_duration_formatted":"5h 3m", 539 | "uncategorized_duration_formatted":"44m 57s", 540 | "business_duration_formatted":"1h 30m", 541 | "communication_and_scheduling_duration_formatted":"3h 34m", 542 | "social_networking_duration_formatted":"1h 33m", 543 | "design_and_composition_duration_formatted":"27m 53s", 544 | "entertainment_duration_formatted":"7m 25s", 545 | "news_duration_formatted":"27m 52s", 546 | "software_development_duration_formatted":"32m 45s", 547 | "reference_and_learning_duration_formatted":"32m 50s", 548 | "shopping_duration_formatted":"1m 25s", 549 | "utilities_duration_formatted":"27m 24s" 550 | }, 551 | { 552 | "id":1464591600, 553 | "date":"2016-05-30", 554 | "productivity_pulse":52, 555 | "very_productive_percentage":32.5, 556 | "productive_percentage":11.2, 557 | "neutral_percentage":14, 558 | "distracting_percentage":18.3, 559 | "very_distracting_percentage":23.9, 560 | "all_productive_percentage":43.8, 561 | "all_distracting_percentage":42.2, 562 | "uncategorized_percentage":7.8, 563 | "business_percentage":13.4, 564 | "communication_and_scheduling_percentage":20.9, 565 | "social_networking_percentage":16.9, 566 | "design_and_composition_percentage":2.3, 567 | "entertainment_percentage":3.6, 568 | "news_percentage":2.4, 569 | "software_development_percentage":16.9, 570 | "reference_and_learning_percentage":5.7, 571 | "shopping_percentage":2.3, 572 | "utilities_percentage":7.9, 573 | "total_hours":11.87, 574 | "very_productive_hours":3.86, 575 | "productive_hours":1.33, 576 | "neutral_hours":1.66, 577 | "distracting_hours":2.18, 578 | "very_distracting_hours":2.83, 579 | "all_productive_hours":5.19, 580 | "all_distracting_hours":5.01, 581 | "uncategorized_hours":0.93, 582 | "business_hours":1.59, 583 | "communication_and_scheduling_hours":2.48, 584 | "social_networking_hours":2.01, 585 | "design_and_composition_hours":0.27, 586 | "entertainment_hours":0.42, 587 | "news_hours":0.28, 588 | "software_development_hours":2, 589 | "reference_and_learning_hours":0.67, 590 | "shopping_hours":0.28, 591 | "utilities_hours":0.94, 592 | "total_duration_formatted":"11h 51m", 593 | "very_productive_duration_formatted":"3h 51m", 594 | "productive_duration_formatted":"1h 20m", 595 | "neutral_duration_formatted":"1h 39m", 596 | "distracting_duration_formatted":"2h 10m", 597 | "very_distracting_duration_formatted":"2h 49m", 598 | "all_productive_duration_formatted":"5h 11m", 599 | "all_distracting_duration_formatted":"5h", 600 | "uncategorized_duration_formatted":"55m 47s", 601 | "business_duration_formatted":"1h 35m", 602 | "communication_and_scheduling_duration_formatted":"2h 28m", 603 | "social_networking_duration_formatted":"2h", 604 | "design_and_composition_duration_formatted":"16m 7s", 605 | "entertainment_duration_formatted":"25m 25s", 606 | "news_duration_formatted":"16m 44s", 607 | "software_development_duration_formatted":"2h", 608 | "reference_and_learning_duration_formatted":"40m 20s", 609 | "shopping_duration_formatted":"16m 32s", 610 | "utilities_duration_formatted":"56m 28s" 611 | }, 612 | { 613 | "id":1464505200, 614 | "date":"2016-05-29", 615 | "productivity_pulse":33, 616 | "very_productive_percentage":17.6, 617 | "productive_percentage":11, 618 | "neutral_percentage":14.7, 619 | "distracting_percentage":0.8, 620 | "very_distracting_percentage":55.8, 621 | "all_productive_percentage":28.6, 622 | "all_distracting_percentage":56.7, 623 | "uncategorized_percentage":11.7, 624 | "business_percentage":3.7, 625 | "communication_and_scheduling_percentage":1.8, 626 | "social_networking_percentage":42.4, 627 | "design_and_composition_percentage":12.3, 628 | "entertainment_percentage":3.2, 629 | "news_percentage":2.5, 630 | "software_development_percentage":1.6, 631 | "reference_and_learning_percentage":5.2, 632 | "shopping_percentage":0, 633 | "utilities_percentage":15.5, 634 | "total_hours":4.37, 635 | "very_productive_hours":0.77, 636 | "productive_hours":0.48, 637 | "neutral_hours":0.64, 638 | "distracting_hours":0.04, 639 | "very_distracting_hours":2.44, 640 | "all_productive_hours":1.25, 641 | "all_distracting_hours":2.47, 642 | "uncategorized_hours":0.51, 643 | "business_hours":0.16, 644 | "communication_and_scheduling_hours":0.08, 645 | "social_networking_hours":1.85, 646 | "design_and_composition_hours":0.54, 647 | "entertainment_hours":0.14, 648 | "news_hours":0.11, 649 | "software_development_hours":0.07, 650 | "reference_and_learning_hours":0.23, 651 | "shopping_hours":0, 652 | "utilities_hours":0.68, 653 | "total_duration_formatted":"4h 22m", 654 | "very_productive_duration_formatted":"46m 8s", 655 | "productive_duration_formatted":"28m 53s", 656 | "neutral_duration_formatted":"38m 33s", 657 | "distracting_duration_formatted":"2m 8s", 658 | "very_distracting_duration_formatted":"2h 26m", 659 | "all_productive_duration_formatted":"1h 15m", 660 | "all_distracting_duration_formatted":"2h 28m", 661 | "uncategorized_duration_formatted":"30m 42s", 662 | "business_duration_formatted":"9m 48s", 663 | "communication_and_scheduling_duration_formatted":"4m 38s", 664 | "social_networking_duration_formatted":"1h 51m", 665 | "design_and_composition_duration_formatted":"32m 14s", 666 | "entertainment_duration_formatted":"8m 25s", 667 | "news_duration_formatted":"6m 40s", 668 | "software_development_duration_formatted":"4m 6s", 669 | "reference_and_learning_duration_formatted":"13m 33s", 670 | "shopping_duration_formatted":"5s", 671 | "utilities_duration_formatted":"40m 41s" 672 | }, 673 | { 674 | "id":1464418800, 675 | "date":"2016-05-28", 676 | "productivity_pulse":19, 677 | "very_productive_percentage":3.6, 678 | "productive_percentage":9.6, 679 | "neutral_percentage":14.1, 680 | "distracting_percentage":6.1, 681 | "very_distracting_percentage":66.6, 682 | "all_productive_percentage":13.1, 683 | "all_distracting_percentage":72.8, 684 | "uncategorized_percentage":5, 685 | "business_percentage":1.3, 686 | "communication_and_scheduling_percentage":4.9, 687 | "social_networking_percentage":51.5, 688 | "design_and_composition_percentage":2, 689 | "entertainment_percentage":4.1, 690 | "news_percentage":15.3, 691 | "software_development_percentage":0.3, 692 | "reference_and_learning_percentage":7.6, 693 | "shopping_percentage":0, 694 | "utilities_percentage":8, 695 | "total_hours":2.38, 696 | "very_productive_hours":0.08, 697 | "productive_hours":0.23, 698 | "neutral_hours":0.34, 699 | "distracting_hours":0.15, 700 | "very_distracting_hours":1.58, 701 | "all_productive_hours":0.31, 702 | "all_distracting_hours":1.73, 703 | "uncategorized_hours":0.12, 704 | "business_hours":0.03, 705 | "communication_and_scheduling_hours":0.12, 706 | "social_networking_hours":1.22, 707 | "design_and_composition_hours":0.05, 708 | "entertainment_hours":0.1, 709 | "news_hours":0.36, 710 | "software_development_hours":0.01, 711 | "reference_and_learning_hours":0.18, 712 | "shopping_hours":0, 713 | "utilities_hours":0.19, 714 | "total_duration_formatted":"2h 22m", 715 | "very_productive_duration_formatted":"5m 5s", 716 | "productive_duration_formatted":"13m 38s", 717 | "neutral_duration_formatted":"20m 8s", 718 | "distracting_duration_formatted":"8m 43s", 719 | "very_distracting_duration_formatted":"1h 35m", 720 | "all_productive_duration_formatted":"18m 43s", 721 | "all_distracting_duration_formatted":"1h 43m", 722 | "uncategorized_duration_formatted":"7m 8s", 723 | "business_duration_formatted":"1m 55s", 724 | "communication_and_scheduling_duration_formatted":"6m 59s", 725 | "social_networking_duration_formatted":"1h 13m", 726 | "design_and_composition_duration_formatted":"2m 48s", 727 | "entertainment_duration_formatted":"5m 48s", 728 | "news_duration_formatted":"21m 49s", 729 | "software_development_duration_formatted":"22s", 730 | "reference_and_learning_duration_formatted":"10m 52s", 731 | "shopping_duration_formatted":"2s", 732 | "utilities_duration_formatted":"11m 25s" 733 | }, 734 | { 735 | "id":1464332400, 736 | "date":"2016-05-27", 737 | "productivity_pulse":48, 738 | "very_productive_percentage":31.7, 739 | "productive_percentage":4, 740 | "neutral_percentage":16, 741 | "distracting_percentage":23.8, 742 | "very_distracting_percentage":24.5, 743 | "all_productive_percentage":35.7, 744 | "all_distracting_percentage":48.3, 745 | "uncategorized_percentage":6.7, 746 | "business_percentage":16.9, 747 | "communication_and_scheduling_percentage":30.9, 748 | "social_networking_percentage":15.6, 749 | "design_and_composition_percentage":2.9, 750 | "entertainment_percentage":4.9, 751 | "news_percentage":3.8, 752 | "software_development_percentage":12, 753 | "reference_and_learning_percentage":2.1, 754 | "shopping_percentage":0.4, 755 | "utilities_percentage":3.9, 756 | "total_hours":7.91, 757 | "very_productive_hours":2.51, 758 | "productive_hours":0.31, 759 | "neutral_hours":1.27, 760 | "distracting_hours":1.88, 761 | "very_distracting_hours":1.94, 762 | "all_productive_hours":2.82, 763 | "all_distracting_hours":3.82, 764 | "uncategorized_hours":0.53, 765 | "business_hours":1.33, 766 | "communication_and_scheduling_hours":2.44, 767 | "social_networking_hours":1.24, 768 | "design_and_composition_hours":0.23, 769 | "entertainment_hours":0.39, 770 | "news_hours":0.3, 771 | "software_development_hours":0.95, 772 | "reference_and_learning_hours":0.16, 773 | "shopping_hours":0.03, 774 | "utilities_hours":0.31, 775 | "total_duration_formatted":"7h 54m", 776 | "very_productive_duration_formatted":"2h 30m", 777 | "productive_duration_formatted":"18m 45s", 778 | "neutral_duration_formatted":"1h 16m", 779 | "distracting_duration_formatted":"1h 52m", 780 | "very_distracting_duration_formatted":"1h 56m", 781 | "all_productive_duration_formatted":"2h 49m", 782 | "all_distracting_duration_formatted":"3h 49m", 783 | "uncategorized_duration_formatted":"31m 45s", 784 | "business_duration_formatted":"1h 20m", 785 | "communication_and_scheduling_duration_formatted":"2h 26m", 786 | "social_networking_duration_formatted":"1h 14m", 787 | "design_and_composition_duration_formatted":"13m 42s", 788 | "entertainment_duration_formatted":"23m 11s", 789 | "news_duration_formatted":"17m 48s", 790 | "software_development_duration_formatted":"56m 50s", 791 | "reference_and_learning_duration_formatted":"9m 52s", 792 | "shopping_duration_formatted":"1m 48s", 793 | "utilities_duration_formatted":"18m 37s" 794 | }, 795 | { 796 | "id":1464246000, 797 | "date":"2016-05-26", 798 | "productivity_pulse":52, 799 | "very_productive_percentage":34.2, 800 | "productive_percentage":9.3, 801 | "neutral_percentage":18.1, 802 | "distracting_percentage":10.6, 803 | "very_distracting_percentage":27.8, 804 | "all_productive_percentage":43.5, 805 | "all_distracting_percentage":38.5, 806 | "uncategorized_percentage":7.7, 807 | "business_percentage":16.6, 808 | "communication_and_scheduling_percentage":18.2, 809 | "social_networking_percentage":20.7, 810 | "design_and_composition_percentage":9.9, 811 | "entertainment_percentage":2.4, 812 | "news_percentage":4.5, 813 | "software_development_percentage":7.7, 814 | "reference_and_learning_percentage":3.6, 815 | "shopping_percentage":0.4, 816 | "utilities_percentage":8.4, 817 | "total_hours":11.27, 818 | "very_productive_hours":3.85, 819 | "productive_hours":1.05, 820 | "neutral_hours":2.04, 821 | "distracting_hours":1.2, 822 | "very_distracting_hours":3.14, 823 | "all_productive_hours":4.9, 824 | "all_distracting_hours":4.33, 825 | "uncategorized_hours":0.87, 826 | "business_hours":1.87, 827 | "communication_and_scheduling_hours":2.05, 828 | "social_networking_hours":2.34, 829 | "design_and_composition_hours":1.12, 830 | "entertainment_hours":0.27, 831 | "news_hours":0.5, 832 | "software_development_hours":0.86, 833 | "reference_and_learning_hours":0.41, 834 | "shopping_hours":0.04, 835 | "utilities_hours":0.94, 836 | "total_duration_formatted":"11h 16m", 837 | "very_productive_duration_formatted":"3h 51m", 838 | "productive_duration_formatted":"1h 2m", 839 | "neutral_duration_formatted":"2h 2m", 840 | "distracting_duration_formatted":"1h 11m", 841 | "very_distracting_duration_formatted":"3h 8m", 842 | "all_productive_duration_formatted":"4h 53m", 843 | "all_distracting_duration_formatted":"4h 20m", 844 | "uncategorized_duration_formatted":"52m 13s", 845 | "business_duration_formatted":"1h 52m", 846 | "communication_and_scheduling_duration_formatted":"2h 2m", 847 | "social_networking_duration_formatted":"2h 20m", 848 | "design_and_composition_duration_formatted":"1h 6m", 849 | "entertainment_duration_formatted":"15m 58s", 850 | "news_duration_formatted":"30m 12s", 851 | "software_development_duration_formatted":"51m 45s", 852 | "reference_and_learning_duration_formatted":"24m 22s", 853 | "shopping_duration_formatted":"2m 35s", 854 | "utilities_duration_formatted":"56m 39s" 855 | }, 856 | { 857 | "id":1464159600, 858 | "date":"2016-05-25", 859 | "productivity_pulse":55, 860 | "very_productive_percentage":33.1, 861 | "productive_percentage":11.8, 862 | "neutral_percentage":21.1, 863 | "distracting_percentage":13.2, 864 | "very_distracting_percentage":20.9, 865 | "all_productive_percentage":44.8, 866 | "all_distracting_percentage":34.1, 867 | "uncategorized_percentage":12.4, 868 | "business_percentage":6.8, 869 | "communication_and_scheduling_percentage":17.8, 870 | "social_networking_percentage":15.5, 871 | "design_and_composition_percentage":4, 872 | "entertainment_percentage":2.1, 873 | "news_percentage":2.6, 874 | "software_development_percentage":22.3, 875 | "reference_and_learning_percentage":2.9, 876 | "shopping_percentage":0.2, 877 | "utilities_percentage":13.5, 878 | "total_hours":8.07, 879 | "very_productive_hours":2.67, 880 | "productive_hours":0.95, 881 | "neutral_hours":1.7, 882 | "distracting_hours":1.06, 883 | "very_distracting_hours":1.68, 884 | "all_productive_hours":3.62, 885 | "all_distracting_hours":2.75, 886 | "uncategorized_hours":1, 887 | "business_hours":0.55, 888 | "communication_and_scheduling_hours":1.44, 889 | "social_networking_hours":1.25, 890 | "design_and_composition_hours":0.32, 891 | "entertainment_hours":0.17, 892 | "news_hours":0.21, 893 | "software_development_hours":1.8, 894 | "reference_and_learning_hours":0.24, 895 | "shopping_hours":0.01, 896 | "utilities_hours":1.09, 897 | "total_duration_formatted":"8h 3m", 898 | "very_productive_duration_formatted":"2h 40m", 899 | "productive_duration_formatted":"56m 58s", 900 | "neutral_duration_formatted":"1h 42m", 901 | "distracting_duration_formatted":"1h 3m", 902 | "very_distracting_duration_formatted":"1h 41m", 903 | "all_productive_duration_formatted":"3h 36m", 904 | "all_distracting_duration_formatted":"2h 44m", 905 | "uncategorized_duration_formatted":"1h", 906 | "business_duration_formatted":"32m 43s", 907 | "communication_and_scheduling_duration_formatted":"1h 26m", 908 | "social_networking_duration_formatted":"1h 14m", 909 | "design_and_composition_duration_formatted":"19m 9s", 910 | "entertainment_duration_formatted":"10m 7s", 911 | "news_duration_formatted":"12m 34s", 912 | "software_development_duration_formatted":"1h 48m", 913 | "reference_and_learning_duration_formatted":"14m 8s", 914 | "shopping_duration_formatted":"47s", 915 | "utilities_duration_formatted":"1h 5m" 916 | } 917 | ] -------------------------------------------------------------------------------- /tests/RescueTime/Tests/Fakes/interval.json: -------------------------------------------------------------------------------- 1 | { 2 | "notes": "data is an array of arrays (rows), column names for rows in row_headers", 3 | "row_headers": [ 4 | "Date", 5 | "Time Spent (seconds)", 6 | "Number of People", 7 | "Activity", 8 | "Category", 9 | "Productivity" 10 | ], 11 | "rows": [ 12 | [ 13 | "2013-08-11T18:00:00", 14 | 91, 15 | 1, 16 | "sublime text 2", 17 | "Editing & IDEs", 18 | 2 19 | ], 20 | [ 21 | "2013-08-11T18:00:00", 22 | 69, 23 | 1, 24 | "iTerm", 25 | "Systems Operations", 26 | 2 27 | ], 28 | [ 29 | "2013-08-11T18:00:00", 30 | 20, 31 | 1, 32 | "rescuetime.com", 33 | "Intelligence", 34 | 2 35 | ], 36 | [ 37 | "2013-08-11T18:00:00", 38 | 12, 39 | 1, 40 | "RescueTime", 41 | "Intelligence", 42 | 2 43 | ], 44 | [ 45 | "2013-08-11T18:00:00", 46 | 7, 47 | 1, 48 | "evernote", 49 | "Intelligence", 50 | 2 51 | ], 52 | [ 53 | "2013-08-11T19:00:00", 54 | 220, 55 | 1, 56 | "php.net", 57 | "Engineering & Technology", 58 | 1 59 | ], 60 | [ 61 | "2013-08-11T19:00:00", 62 | 191, 63 | 1, 64 | "spotify", 65 | "General Entertainment", 66 | -2 67 | ], 68 | [ 69 | "2013-08-11T19:00:00", 70 | 32, 71 | 1, 72 | "mirkoborivojevic.com", 73 | "Uncategorized", 74 | 0 75 | ], 76 | [ 77 | "2013-08-11T19:00:00", 78 | 10, 79 | 1, 80 | "Gmail", 81 | "Email", 82 | 0 83 | ], 84 | [ 85 | "2013-08-11T19:00:00", 86 | 6, 87 | 1, 88 | "google.rs", 89 | "Search", 90 | 0 91 | ], 92 | [ 93 | "2013-08-11T20:00:00", 94 | 175, 95 | 1, 96 | "teamviewer", 97 | "General Utilities", 98 | 1 99 | ], 100 | [ 101 | "2013-08-11T20:00:00", 102 | 60, 103 | 1, 104 | "github.com", 105 | "General Software Development", 106 | 2 107 | ], 108 | [ 109 | "2013-08-11T20:00:00", 110 | 23, 111 | 1, 112 | "Steam", 113 | "Games", 114 | -2 115 | ], 116 | [ 117 | "2013-08-12T21:00:00", 118 | 3, 119 | 1, 120 | "gist.github.com", 121 | "General Software Development", 122 | 2 123 | ] 124 | ] 125 | } -------------------------------------------------------------------------------- /tests/RescueTime/Tests/Fakes/member.json: -------------------------------------------------------------------------------- 1 | { 2 | "notes": "data is an array of arrays (rows), column names for rows in row_headers", 3 | "row_headers": [ 4 | "Person", 5 | "Time Spent (seconds)", 6 | "Activity", 7 | "Category", 8 | "Productivity" 9 | ], 10 | "rows": [ 11 | [ 12 | "Mirko", 13 | 10038, 14 | "sublime text 2", 15 | "Editing & IDEs", 16 | 2 17 | ], 18 | [ 19 | "Mirko", 20 | 1810, 21 | "basecamp.com", 22 | "Design & Planning", 23 | 2 24 | ], 25 | [ 26 | "Mirko", 27 | 1670, 28 | "iTerm", 29 | "Systems Operations", 30 | 2 31 | ], 32 | [ 33 | "Mirko", 34 | 1300, 35 | "Gmail", 36 | "Email", 37 | 0 38 | ], 39 | [ 40 | "Mirko", 41 | 857, 42 | "rescuetime.com", 43 | "Intelligence", 44 | 2 45 | ], 46 | [ 47 | "Mirko", 48 | 512, 49 | "kmplayer", 50 | "General Entertainment", 51 | -2 52 | ], 53 | [ 54 | "Mirko", 55 | 423, 56 | "mirkoborivojevic.com", 57 | "Uncategorized", 58 | 0 59 | ], 60 | [ 61 | "Mirko", 62 | 420, 63 | "mysqlworkbench", 64 | "General Software Development", 65 | 2 66 | ], 67 | [ 68 | "Mirko", 69 | 419, 70 | "Hosted Google Docs", 71 | "Writing", 72 | 2 73 | ], 74 | [ 75 | "Mirko", 76 | 418, 77 | "evernote", 78 | "Intelligence", 79 | 2 80 | ], 81 | [ 82 | "Mirko", 83 | 365, 84 | "google.com/analytics", 85 | "General Software Development", 86 | 2 87 | ], 88 | [ 89 | "Mirko", 90 | 313, 91 | "github.com", 92 | "General Software Development", 93 | 2 94 | ], 95 | [ 96 | "Mirko", 97 | 305, 98 | "google.rs", 99 | "Search", 100 | 0 101 | ], 102 | [ 103 | "Mirko", 104 | 291, 105 | "teamviewer", 106 | "General Utilities", 107 | 1 108 | ], 109 | [ 110 | "Mirko", 111 | 260, 112 | "spotify", 113 | "General Entertainment", 114 | -2 115 | ], 116 | [ 117 | "Mirko", 118 | 141, 119 | "iTunes", 120 | "Music", 121 | -2 122 | ], 123 | [ 124 | "Mirko", 125 | 94, 126 | "Twitter", 127 | "General Social Networking", 128 | -2 129 | ] 130 | ] 131 | } -------------------------------------------------------------------------------- /tests/RescueTime/Tests/Fakes/rank.json: -------------------------------------------------------------------------------- 1 | { 2 | "notes": "data is an array of arrays (rows), column names for rows in row_headers", 3 | "row_headers": [ 4 | "Rank", 5 | "Time Spent (seconds)", 6 | "Number of People", 7 | "Activity", 8 | "Category", 9 | "Productivity" 10 | ], 11 | "rows": [ 12 | [ 13 | 1, 14 | 10038, 15 | 1, 16 | "sublime text 2", 17 | "Editing & IDEs", 18 | 2 19 | ], 20 | [ 21 | 2, 22 | 1810, 23 | 1, 24 | "basecamp.com", 25 | "Design & Planning", 26 | 2 27 | ], 28 | [ 29 | 3, 30 | 1300, 31 | 1, 32 | "Gmail", 33 | "Email", 34 | 0 35 | ], 36 | [ 37 | 4, 38 | 857, 39 | 1, 40 | "rescuetime.com", 41 | "Intelligence", 42 | 2 43 | ], 44 | [ 45 | 5, 46 | 512, 47 | 1, 48 | "kmplayer", 49 | "General Entertainment", 50 | -2 51 | ], 52 | [ 53 | 6, 54 | 423, 55 | 1, 56 | "mirkoborivojevic.com", 57 | "Uncategorized", 58 | 0 59 | ], 60 | [ 61 | 7, 62 | 420, 63 | 1, 64 | "mysqlworkbench", 65 | "General Software Development", 66 | 2 67 | ], 68 | [ 69 | 8, 70 | 419, 71 | 1, 72 | "Hosted Google Docs", 73 | "Writing", 74 | 2 75 | ], 76 | [ 77 | 9, 78 | 418, 79 | 1, 80 | "evernote", 81 | "Intelligence", 82 | 2 83 | ], 84 | [ 85 | 10, 86 | 365, 87 | 1, 88 | "google.com/analytics", 89 | "General Software Development", 90 | 2 91 | ], 92 | [ 93 | 11, 94 | 313, 95 | 1, 96 | "github.com", 97 | "General Software Development", 98 | 2 99 | ], 100 | [ 101 | 12, 102 | 305, 103 | 1, 104 | "google.rs", 105 | "Search", 106 | 0 107 | ], 108 | [ 109 | 13, 110 | 291, 111 | 1, 112 | "teamviewer", 113 | "General Utilities", 114 | 1 115 | ], 116 | [ 117 | 14, 118 | 260, 119 | 1, 120 | "spotify", 121 | "General Entertainment", 122 | -2 123 | ], 124 | [ 125 | 15, 126 | 141, 127 | 1, 128 | "iTunes", 129 | "Music", 130 | -2 131 | ] 132 | ] 133 | } -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | add('RescueTime\Tests', __DIR__); 4 | --------------------------------------------------------------------------------