harvestr 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History +1 -0
- data/License +20 -0
- data/Notes +0 -0
- data/README.rdoc +17 -0
- data/Rakefile +102 -0
- data/VERSION.yml +4 -0
- data/examples/time_tracking.rb +13 -0
- data/lib/harvest/base.rb +29 -0
- data/lib/harvest/reports.rb +34 -0
- data/lib/harvest/time_tracking.rb +44 -0
- data/lib/harvestr.rb +60 -0
- data/test/fixtures/daily.xml +62 -0
- data/test/fixtures/entry.xml +16 -0
- data/test/fixtures/project_entries.xml +55 -0
- data/test/fixtures/rate_limit_status.xml +7 -0
- data/test/fixtures/timer_toggle.xml +17 -0
- data/test/fixtures/update_entry.xml +17 -0
- data/test/harvestr/base_test.rb +22 -0
- data/test/harvestr/reports_test.rb +21 -0
- data/test/harvestr/time_tracking_test.rb +72 -0
- data/test/test_helper.rb +32 -0
- metadata +150 -0
data/History
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1 - initial release
|
data/License
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Squeejee
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Notes
ADDED
File without changes
|
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= harvestr
|
2
|
+
|
3
|
+
A Ruby wrapper for the Harvest and Co-op APIs.
|
4
|
+
|
5
|
+
== examples
|
6
|
+
|
7
|
+
See the examples directory.
|
8
|
+
|
9
|
+
http://github.com/squeejee/harvestr/tree/master/examples
|
10
|
+
|
11
|
+
== docs
|
12
|
+
|
13
|
+
http://rdoc.info/projects/squeejee/harvestr
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2009 Squeejee. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "harvestr"
|
8
|
+
gem.summary = %Q{wrapper for the Harvest api}
|
9
|
+
gem.email = "jim@squeejee.com"
|
10
|
+
gem.homepage = "http://github.com/squeejee/harvestr"
|
11
|
+
gem.authors = ["Wynn Netherland", "Jim Mulholland", "Jason Derrett"]
|
12
|
+
gem.rubyforge_project = "harvestr"
|
13
|
+
gem.files = FileList["[A-Z]*", "{examples,lib,test}/**/*"]
|
14
|
+
|
15
|
+
gem.add_dependency('mash', '0.0.3')
|
16
|
+
gem.add_dependency('httparty', '0.4.3')
|
17
|
+
|
18
|
+
gem.add_development_dependency('thoughtbot-shoulda')
|
19
|
+
gem.add_development_dependency('jeremymcanally-matchy')
|
20
|
+
gem.add_development_dependency('mocha')
|
21
|
+
gem.add_development_dependency('fakeweb', '1.2.5')
|
22
|
+
gem.add_development_dependency('mash')
|
23
|
+
end
|
24
|
+
rescue LoadError
|
25
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/*_test.rb'
|
32
|
+
test.verbose = false
|
33
|
+
end
|
34
|
+
|
35
|
+
begin
|
36
|
+
require 'rcov/rcovtask'
|
37
|
+
Rcov::RcovTask.new do |test|
|
38
|
+
test.libs << 'test'
|
39
|
+
test.pattern = 'test/**/*_test.rb'
|
40
|
+
test.verbose = true
|
41
|
+
end
|
42
|
+
rescue LoadError
|
43
|
+
task :rcov do
|
44
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
task :default => :test
|
50
|
+
|
51
|
+
require 'rake/rdoctask'
|
52
|
+
Rake::RDocTask.new do |rdoc|
|
53
|
+
if File.exist?('VERSION.yml')
|
54
|
+
config = YAML.load(File.read('VERSION.yml'))
|
55
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
56
|
+
else
|
57
|
+
version = ""
|
58
|
+
end
|
59
|
+
|
60
|
+
rdoc.rdoc_dir = 'rdoc'
|
61
|
+
rdoc.title = "harvestr #{version}"
|
62
|
+
rdoc.rdoc_files.include('README*')
|
63
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
64
|
+
end
|
65
|
+
|
66
|
+
begin
|
67
|
+
require 'rake/contrib/sshpublisher'
|
68
|
+
namespace :rubyforge do
|
69
|
+
|
70
|
+
desc "Release gem and RDoc documentation to RubyForge"
|
71
|
+
task :release => ["rubyforge:release:gem", "rubyforge:release:website", "rubyforge:release:docs"]
|
72
|
+
|
73
|
+
namespace :release do
|
74
|
+
desc "Publish RDoc to RubyForge."
|
75
|
+
task :docs => [:rdoc] do
|
76
|
+
config = YAML.load(
|
77
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
78
|
+
)
|
79
|
+
|
80
|
+
host = "#{config['username']}@rubyforge.org"
|
81
|
+
remote_dir = "/var/www/gforge-projects/harvestr/rdoc"
|
82
|
+
local_dir = 'rdoc'
|
83
|
+
|
84
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
85
|
+
end
|
86
|
+
|
87
|
+
task :website do
|
88
|
+
config = YAML.load(
|
89
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
90
|
+
)
|
91
|
+
|
92
|
+
host = "#{config['username']}@rubyforge.org"
|
93
|
+
remote_dir = "/var/www/gforge-projects/harvestr/"
|
94
|
+
local_dir = 'website'
|
95
|
+
|
96
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
rescue LoadError
|
101
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
102
|
+
end
|
data/VERSION.yml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'lib/harvestr'
|
2
|
+
Harvest.domain = 'yourdomain'
|
3
|
+
Harvest.email = 'you@example.com'
|
4
|
+
Harvest.password = 'yourpass'
|
5
|
+
client = Harvest::TimeTracking.new
|
6
|
+
|
7
|
+
# add a new entry
|
8
|
+
client.add({:project_id => 408960, :task_id => 333866, :notes => 'Writing TPS reports', :hours => 1.21})
|
9
|
+
#=> <Mash client="Pengwynn Group" hours=1.21 id=14319479 notes="Writing TPS reports" project="Internal" project_id="408960" task="Project Management" task_id="333866">
|
10
|
+
|
11
|
+
# toggle an entry
|
12
|
+
client.toggle(14319479)
|
13
|
+
#=> <Mash client="Pengwynn Group" hours=1.21 id=14319479 notes="Writing TPS reports" project="Internal" project_id="408960" task="Project Management" task_id="333866" timer_started_at=Sat Aug 01 19:14:07 UTC 2009>
|
data/lib/harvest/base.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Harvest
|
2
|
+
class Base
|
3
|
+
include HTTParty
|
4
|
+
format :xml
|
5
|
+
headers({'Accept' => 'text/xml'})
|
6
|
+
|
7
|
+
attr_accessor :domain, :email, :password, :use_ssl
|
8
|
+
|
9
|
+
def initialize(options={})
|
10
|
+
@domain = options[:domain] ||= Harvest.domain
|
11
|
+
@email = options[:email] ||= Harvest.email
|
12
|
+
@password = options[:password] ||= Harvest.password
|
13
|
+
@use_ssl = options[:use_ssl] ||= Harvest.use_ssl
|
14
|
+
|
15
|
+
self.class.base_uri "#{@domain}.harvestapp.com"
|
16
|
+
self.class.basic_auth(@email, @password)
|
17
|
+
end
|
18
|
+
|
19
|
+
def rate_limit_status
|
20
|
+
request = Mash.new(self.class.get('/account/rate_limit_status')).request
|
21
|
+
%w(count time_frame_limit max_calls lockout_seconds).each do |key|
|
22
|
+
request[key] = request[key].to_i
|
23
|
+
end
|
24
|
+
request
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Harvest
|
2
|
+
class Reports < Harvest::Base
|
3
|
+
# GET /projects
|
4
|
+
def projects
|
5
|
+
Mash.new(self.class.get("/projects")).projects
|
6
|
+
end
|
7
|
+
|
8
|
+
# GET /projects/#{project_id}/entries?from=YYYYMMDD&to=YYYYMMDD
|
9
|
+
def project_entries(project_id, from, to, user_id=nil)
|
10
|
+
opts = {:from => from, :to => to}
|
11
|
+
opts[:user_id] = user_id if user_id
|
12
|
+
Mash.new(self.class.get("/projects/#{project_id}/entries", :query => opts)).day_entries
|
13
|
+
end
|
14
|
+
|
15
|
+
def total_project_hours(project_id, from, to, user_id=nil)
|
16
|
+
pe = project_entries(project_id, from, to, user_id)
|
17
|
+
total = 0.0
|
18
|
+
if pe
|
19
|
+
pe.each {|msh| total += msh.hours}
|
20
|
+
end
|
21
|
+
total
|
22
|
+
end
|
23
|
+
|
24
|
+
# [['proj1', 25.5], ['proj2', 33.3]]
|
25
|
+
def project_totals(from=7.days.ago, to=Date.today)
|
26
|
+
@pt = []
|
27
|
+
projects.each do |p|
|
28
|
+
hrs = total_project_hours(p.id, from, to)
|
29
|
+
@pt << [p.name, hrs] if hrs > 0
|
30
|
+
end
|
31
|
+
@pt
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Harvest
|
2
|
+
class TimeTracking < Harvest::Base
|
3
|
+
|
4
|
+
# GET /daily
|
5
|
+
# GET /daily/#{day_of_the_year}/#{year}
|
6
|
+
def daily(date=nil)
|
7
|
+
date = DateTime.parse(date) if date.is_a?(String)
|
8
|
+
if date.respond_to?(:yday)
|
9
|
+
response = self.class.get("/daily/#{date.yday}/#{date.year}")
|
10
|
+
else
|
11
|
+
response = self.class.get('/daily')
|
12
|
+
end
|
13
|
+
response
|
14
|
+
end
|
15
|
+
|
16
|
+
# GET /daily/show/#{day_entry_id}
|
17
|
+
def entry(id)
|
18
|
+
Mash.new(self.class.get("/daily/show/#{id}")).add.day_entry
|
19
|
+
end
|
20
|
+
|
21
|
+
# GET /daily/timer/#{day_entry_id}
|
22
|
+
def toggle(id)
|
23
|
+
Mash.new(self.class.get("/daily/timer/#{id}")).timer.day_entry
|
24
|
+
end
|
25
|
+
|
26
|
+
# POST /daily/add
|
27
|
+
def add(body)
|
28
|
+
Mash.new(self.class.post("/daily/add", :body => body)).add.day_entry
|
29
|
+
end
|
30
|
+
|
31
|
+
# DELETE /daily/delete/#{day_entry_id}
|
32
|
+
def delete(id)
|
33
|
+
Mash.new(self.class.delete("/daily/delete/#{id}"))
|
34
|
+
end
|
35
|
+
|
36
|
+
# POST /daily/update/#{day_entry_id}
|
37
|
+
def update(id, body)
|
38
|
+
# current_entry = entry(id)
|
39
|
+
# body = current_entry.merge(Mash.new body.to_hash)
|
40
|
+
# puts body.inspect
|
41
|
+
Mash.new(self.class.post("/daily/update/#{id}", :body => body)).add.day_entry
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/harvestr.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
gem 'mash', '0.0.3'
|
5
|
+
require 'mash'
|
6
|
+
|
7
|
+
gem 'httparty', '0.4.3'
|
8
|
+
require 'httparty'
|
9
|
+
|
10
|
+
class APIKeyNotSet < StandardError; end
|
11
|
+
|
12
|
+
module Harvest
|
13
|
+
|
14
|
+
def self.domain=(value)
|
15
|
+
@domain = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.domain
|
19
|
+
@domain
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.email=(value)
|
23
|
+
@email = value
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.email
|
27
|
+
@email
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.password=(value)
|
31
|
+
@password = value
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.password
|
35
|
+
@password
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.use_ssl=(value)
|
39
|
+
@use_ssl = !!value
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.use_ssl
|
43
|
+
@use_ssl ||= false
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.default_options
|
47
|
+
{
|
48
|
+
:domain => @domain,
|
49
|
+
:email => @email,
|
50
|
+
:password => @password,
|
51
|
+
:use_ssl => @use_ssl
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
directory = File.expand_path(File.dirname(__FILE__))
|
57
|
+
|
58
|
+
require File.join(directory, 'harvest', 'base')
|
59
|
+
require File.join(directory, 'harvest', 'time_tracking')
|
60
|
+
require File.join(directory, 'harvest', 'reports')
|
@@ -0,0 +1,62 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<daily>
|
3
|
+
<for_day type="date">Sat, 01 Aug 2009</for_day>
|
4
|
+
<day_entries>
|
5
|
+
<day_entry>
|
6
|
+
<id type="integer">14316546</id>
|
7
|
+
<spent_at type="date">2009-08-01</spent_at>
|
8
|
+
<user_id type="integer">86342</user_id>
|
9
|
+
<client>Pengwynn Group</client>
|
10
|
+
<project_id>408960</project_id>
|
11
|
+
<project>Internal</project>
|
12
|
+
<task_id>333865</task_id>
|
13
|
+
<task>Admin</task>
|
14
|
+
<hours type="float">1.34</hours>
|
15
|
+
<notes></notes>
|
16
|
+
<created_at type="datetime">Sat, 01 Aug 2009 02:19:00 +0000</created_at>
|
17
|
+
</day_entry>
|
18
|
+
<day_entry>
|
19
|
+
<id type="integer">14319479</id>
|
20
|
+
<spent_at type="date">2009-08-01</spent_at>
|
21
|
+
<user_id type="integer">86342</user_id>
|
22
|
+
<client>Pengwynn Group</client>
|
23
|
+
<project_id>408960</project_id>
|
24
|
+
<project>Internal</project>
|
25
|
+
<task_id>333866</task_id>
|
26
|
+
<task>Project Management</task>
|
27
|
+
<hours type="float">1.34</hours>
|
28
|
+
<notes></notes>
|
29
|
+
<created_at type="datetime">Sat, 01 Aug 2009 19:09:03 +0000</created_at>
|
30
|
+
</day_entry>
|
31
|
+
</day_entries>
|
32
|
+
<projects>
|
33
|
+
<project>
|
34
|
+
<name>Internal</name>
|
35
|
+
<code></code>
|
36
|
+
<id type="integer">408960</id>
|
37
|
+
<client>Pengwynn Group</client>
|
38
|
+
<tasks>
|
39
|
+
<task>
|
40
|
+
<name>Admin</name>
|
41
|
+
<id type="integer">333865</id>
|
42
|
+
<billable type="boolean">false</billable>
|
43
|
+
</task>
|
44
|
+
<task>
|
45
|
+
<name>Business Development</name>
|
46
|
+
<id type="integer">333867</id>
|
47
|
+
<billable type="boolean">false</billable>
|
48
|
+
</task>
|
49
|
+
<task>
|
50
|
+
<name>Project Management</name>
|
51
|
+
<id type="integer">333866</id>
|
52
|
+
<billable type="boolean">false</billable>
|
53
|
+
</task>
|
54
|
+
<task>
|
55
|
+
<name>Vacation</name>
|
56
|
+
<id type="integer">333868</id>
|
57
|
+
<billable type="boolean">false</billable>
|
58
|
+
</task>
|
59
|
+
</tasks>
|
60
|
+
</project>
|
61
|
+
</projects>
|
62
|
+
</daily>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<add>
|
3
|
+
<day_entry>
|
4
|
+
<id type="integer">14316546</id>
|
5
|
+
<spent_at type="date">2009-08-01</spent_at>
|
6
|
+
<user_id type="integer">86342</user_id>
|
7
|
+
<client>Pengwynn Group</client>
|
8
|
+
<project_id>408960</project_id>
|
9
|
+
<project>Internal</project>
|
10
|
+
<task_id>333865</task_id>
|
11
|
+
<task>Admin</task>
|
12
|
+
<hours type="float">0.00</hours>
|
13
|
+
<notes></notes>
|
14
|
+
<created_at type="datetime">Sat, 01 Aug 2009 02:19:00 +0000</created_at>
|
15
|
+
</day_entry>
|
16
|
+
</add>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<day-entries type="array">
|
3
|
+
<day-entry>
|
4
|
+
<created-at type="datetime">2009-08-01T02:21:32Z</created-at>
|
5
|
+
<hours type="decimal">4.5</hours>
|
6
|
+
<id type="integer">14316565</id>
|
7
|
+
<is-billed type="boolean">false</is-billed>
|
8
|
+
<is-closed type="boolean">false</is-closed>
|
9
|
+
<notes>This is a test</notes>
|
10
|
+
<project-id type="integer">408960</project-id>
|
11
|
+
<spent-at type="date">2009-07-31</spent-at>
|
12
|
+
<task-id type="integer">333865</task-id>
|
13
|
+
<timer-started-at type="datetime" nil="true"></timer-started-at>
|
14
|
+
<user-id type="integer">86342</user-id>
|
15
|
+
</day-entry>
|
16
|
+
<day-entry>
|
17
|
+
<created-at type="datetime">2009-08-01T02:21:46Z</created-at>
|
18
|
+
<hours type="decimal">2.0</hours>
|
19
|
+
<id type="integer">14316569</id>
|
20
|
+
<is-billed type="boolean">false</is-billed>
|
21
|
+
<is-closed type="boolean">false</is-closed>
|
22
|
+
<notes>Signing contracts</notes>
|
23
|
+
<project-id type="integer">408960</project-id>
|
24
|
+
<spent-at type="date">2009-07-31</spent-at>
|
25
|
+
<task-id type="integer">333867</task-id>
|
26
|
+
<timer-started-at type="datetime" nil="true"></timer-started-at>
|
27
|
+
<user-id type="integer">86342</user-id>
|
28
|
+
</day-entry>
|
29
|
+
<day-entry>
|
30
|
+
<created-at type="datetime">2009-08-01T02:19:00Z</created-at>
|
31
|
+
<hours type="decimal">0.0</hours>
|
32
|
+
<id type="integer">14316546</id>
|
33
|
+
<is-billed type="boolean">false</is-billed>
|
34
|
+
<is-closed type="boolean">false</is-closed>
|
35
|
+
<notes nil="true"></notes>
|
36
|
+
<project-id type="integer">408960</project-id>
|
37
|
+
<spent-at type="date">2009-08-01</spent-at>
|
38
|
+
<task-id type="integer">333865</task-id>
|
39
|
+
<timer-started-at type="datetime" nil="true"></timer-started-at>
|
40
|
+
<user-id type="integer">86342</user-id>
|
41
|
+
</day-entry>
|
42
|
+
<day-entry>
|
43
|
+
<created-at type="datetime">2009-08-01T19:09:03Z</created-at>
|
44
|
+
<hours type="decimal">1.34</hours>
|
45
|
+
<id type="integer">14319479</id>
|
46
|
+
<is-billed type="boolean">false</is-billed>
|
47
|
+
<is-closed type="boolean">false</is-closed>
|
48
|
+
<notes></notes>
|
49
|
+
<project-id type="integer">408960</project-id>
|
50
|
+
<spent-at type="date">2009-08-01</spent-at>
|
51
|
+
<task-id type="integer">333866</task-id>
|
52
|
+
<timer-started-at type="datetime" nil="true"></timer-started-at>
|
53
|
+
<user-id type="integer">86342</user-id>
|
54
|
+
</day-entry>
|
55
|
+
</day-entries>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<timer>
|
3
|
+
<day_entry>
|
4
|
+
<id type="integer">14319479</id>
|
5
|
+
<spent_at type="date">2009-08-01</spent_at>
|
6
|
+
<user_id type="integer">86342</user_id>
|
7
|
+
<client>Pengwynn Group</client>
|
8
|
+
<project_id>408960</project_id>
|
9
|
+
<project>Internal</project>
|
10
|
+
<task_id>333866</task_id>
|
11
|
+
<task>Project Management</task>
|
12
|
+
<hours type="float">0.00</hours>
|
13
|
+
<notes></notes>
|
14
|
+
<created_at type="datetime">Sat, 01 Aug 2009 19:09:03 +0000</created_at>
|
15
|
+
<timer_started_at type="datetime">Sat, 01 Aug 2009 23:16:51 +0000</timer_started_at>
|
16
|
+
</day_entry>
|
17
|
+
</timer>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<add>
|
3
|
+
<day_entry>
|
4
|
+
<id type="integer">14319479</id>
|
5
|
+
<spent_at type="date">2009-08-01</spent_at>
|
6
|
+
<user_id type="integer">86342</user_id>
|
7
|
+
<client>Pengwynn Group</client>
|
8
|
+
<project_id>408960</project_id>
|
9
|
+
<project>Internal</project>
|
10
|
+
<task_id>333866</task_id>
|
11
|
+
<task>Project Management</task>
|
12
|
+
<hours type="float">1.07</hours>
|
13
|
+
<notes>New notes</notes>
|
14
|
+
<created_at type="datetime">Sat, 01 Aug 2009 19:09:03 +0000</created_at>
|
15
|
+
<timer_started_at type="datetime">Sat, 01 Aug 2009 23:37:29 +0000</timer_started_at>
|
16
|
+
</day_entry>
|
17
|
+
</add>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class BaseTest < Test::Unit::TestCase
|
4
|
+
include Harvest
|
5
|
+
|
6
|
+
context "When using the API" do
|
7
|
+
setup do
|
8
|
+
Harvest.domain = 'pengwynn'
|
9
|
+
Harvest.email = 'pengwynn@example.com'
|
10
|
+
Harvest.password = 'OU812'
|
11
|
+
@client = Harvest::Base.new
|
12
|
+
end
|
13
|
+
|
14
|
+
should "fetch rate limit status" do
|
15
|
+
stub_get "http://pengwynn%40example.com:OU812@pengwynn.harvestapp.com/account/rate_limit_status", 'rate_limit_status.xml'
|
16
|
+
status = @client.rate_limit_status
|
17
|
+
status.max_calls.should == 40
|
18
|
+
status.lockout_seconds.should == 300
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class ReportsTest < Test::Unit::TestCase
|
4
|
+
include Harvest
|
5
|
+
|
6
|
+
context "When using the TimeTracking API" do
|
7
|
+
setup do
|
8
|
+
Harvest.domain = 'pengwynn'
|
9
|
+
Harvest.email = 'pengwynn@example.com'
|
10
|
+
Harvest.password = 'OU812'
|
11
|
+
@client = Harvest::Reports.new
|
12
|
+
end
|
13
|
+
|
14
|
+
should "retrieve project entries for a time range as string values" do
|
15
|
+
stub_get "http://pengwynn%40example.com:OU812@pengwynn.harvestapp.com/projects/408960/entries?from=20090730&to=20090802", 'project_entries.xml'
|
16
|
+
entries = @client.project_entries(408960, '20090730', '20090802')
|
17
|
+
entries.size.should == 4
|
18
|
+
entries.first.hours.should == 4.5
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class TimeTrackingTest < Test::Unit::TestCase
|
4
|
+
include Harvest
|
5
|
+
|
6
|
+
context "When using the TimeTracking API" do
|
7
|
+
setup do
|
8
|
+
Harvest.domain = 'pengwynn'
|
9
|
+
Harvest.email = 'pengwynn@example.com'
|
10
|
+
Harvest.password = 'OU812'
|
11
|
+
@client = Harvest::TimeTracking.new
|
12
|
+
end
|
13
|
+
|
14
|
+
should "retrieve time entries for today" do
|
15
|
+
stub_get "http://pengwynn%40example.com:OU812@pengwynn.harvestapp.com/daily", 'daily.xml'
|
16
|
+
response = @client.daily
|
17
|
+
entries = response.day_entries
|
18
|
+
entries.size.should == 2
|
19
|
+
entries.first.project.should == 'Internal'
|
20
|
+
end
|
21
|
+
|
22
|
+
should "retrieve time entries for a given day as a string" do
|
23
|
+
stub_get "http://pengwynn%40example.com:OU812@pengwynn.harvestapp.com/daily/213/2009", 'daily.xml'
|
24
|
+
response = @client.daily('2009-8-1')
|
25
|
+
entries = response.day_entries
|
26
|
+
entries.class.should == Array
|
27
|
+
end
|
28
|
+
|
29
|
+
should "retrieve time entries for a given day as a DateTime" do
|
30
|
+
stub_get "http://pengwynn%40example.com:OU812@pengwynn.harvestapp.com/daily/213/2009", 'daily.xml'
|
31
|
+
response = @client.daily(DateTime.parse('2009-08-01'))
|
32
|
+
entries = response.day_entries
|
33
|
+
entries.class.should == Array
|
34
|
+
end
|
35
|
+
|
36
|
+
should "retrieve a single entry" do
|
37
|
+
stub_get "http://pengwynn%40example.com:OU812@pengwynn.harvestapp.com/daily/show/14316546", 'entry.xml'
|
38
|
+
entry = @client.entry(14316546)
|
39
|
+
entry.id.should == 14316546
|
40
|
+
entry.client.should == 'Pengwynn Group'
|
41
|
+
end
|
42
|
+
|
43
|
+
should "toggle the timer" do
|
44
|
+
stub_get "http://pengwynn%40example.com:OU812@pengwynn.harvestapp.com/daily/timer/14319479", 'entry.xml'
|
45
|
+
entry = @client.toggle(14319479)
|
46
|
+
entry.id.should == 14319479
|
47
|
+
end
|
48
|
+
|
49
|
+
should "create a new entry" do
|
50
|
+
stub_post "http://pengwynn%40example.com:OU812@pengwynn.harvestapp.com/daily/add", 'entry.xml'
|
51
|
+
entry = @client.add({
|
52
|
+
:project_id => 408960,
|
53
|
+
:task_id => 333865,
|
54
|
+
:hours => 3.24,
|
55
|
+
:spent_at => Time.now
|
56
|
+
})
|
57
|
+
entry.id.should == 14316546
|
58
|
+
end
|
59
|
+
|
60
|
+
# should "delete an entry" do
|
61
|
+
# FakeWeb.register_uri(:delete, "http://pengwynn%40example.com:OU812@pengwynn.harvestapp.com/delete/14316546", {})
|
62
|
+
# @client.delete(14316546)
|
63
|
+
# end
|
64
|
+
|
65
|
+
should "update an entry" do
|
66
|
+
stub_post "http://pengwynn%40example.com:OU812@pengwynn.harvestapp.com/daily/update/14316546", 'entry.xml'
|
67
|
+
entry = @client.update(14316546, {:notes => 'Writing TPS reports'})
|
68
|
+
entry.id.should == 14316546
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'matchy'
|
5
|
+
require 'mocha'
|
6
|
+
require 'fakeweb'
|
7
|
+
|
8
|
+
FakeWeb.allow_net_connect = false
|
9
|
+
|
10
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
11
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
12
|
+
require 'harvestr'
|
13
|
+
|
14
|
+
class Test::Unit::TestCase
|
15
|
+
end
|
16
|
+
|
17
|
+
def fixture_file(filename)
|
18
|
+
return '' if filename == ''
|
19
|
+
file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/' + filename)
|
20
|
+
File.read(file_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
def stub_get(url, filename, status=nil)
|
24
|
+
options = {:body => fixture_file(filename)}
|
25
|
+
options.merge!({:status => status}) unless status.nil?
|
26
|
+
|
27
|
+
FakeWeb.register_uri(:get, url, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def stub_post(url, filename)
|
31
|
+
FakeWeb.register_uri(:post, url, :body => fixture_file(filename))
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: harvestr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Wynn Netherland
|
8
|
+
- Jim Mulholland
|
9
|
+
- Jason Derrett
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
|
14
|
+
date: 2009-11-16 00:00:00 -06:00
|
15
|
+
default_executable:
|
16
|
+
dependencies:
|
17
|
+
- !ruby/object:Gem::Dependency
|
18
|
+
name: mash
|
19
|
+
type: :runtime
|
20
|
+
version_requirement:
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 0.0.3
|
26
|
+
version:
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: httparty
|
29
|
+
type: :runtime
|
30
|
+
version_requirement:
|
31
|
+
version_requirements: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 0.4.3
|
36
|
+
version:
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: thoughtbot-shoulda
|
39
|
+
type: :development
|
40
|
+
version_requirement:
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: jeremymcanally-matchy
|
49
|
+
type: :development
|
50
|
+
version_requirement:
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: mocha
|
59
|
+
type: :development
|
60
|
+
version_requirement:
|
61
|
+
version_requirements: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: fakeweb
|
69
|
+
type: :development
|
70
|
+
version_requirement:
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.2.5
|
76
|
+
version:
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: mash
|
79
|
+
type: :development
|
80
|
+
version_requirement:
|
81
|
+
version_requirements: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: "0"
|
86
|
+
version:
|
87
|
+
description:
|
88
|
+
email: jim@squeejee.com
|
89
|
+
executables: []
|
90
|
+
|
91
|
+
extensions: []
|
92
|
+
|
93
|
+
extra_rdoc_files:
|
94
|
+
- README.rdoc
|
95
|
+
files:
|
96
|
+
- History
|
97
|
+
- License
|
98
|
+
- Notes
|
99
|
+
- README.rdoc
|
100
|
+
- Rakefile
|
101
|
+
- VERSION.yml
|
102
|
+
- examples/time_tracking.rb
|
103
|
+
- lib/harvest/base.rb
|
104
|
+
- lib/harvest/reports.rb
|
105
|
+
- lib/harvest/time_tracking.rb
|
106
|
+
- lib/harvestr.rb
|
107
|
+
- test/fixtures/daily.xml
|
108
|
+
- test/fixtures/entry.xml
|
109
|
+
- test/fixtures/project_entries.xml
|
110
|
+
- test/fixtures/rate_limit_status.xml
|
111
|
+
- test/fixtures/timer_toggle.xml
|
112
|
+
- test/fixtures/update_entry.xml
|
113
|
+
- test/harvestr/base_test.rb
|
114
|
+
- test/harvestr/reports_test.rb
|
115
|
+
- test/harvestr/time_tracking_test.rb
|
116
|
+
- test/test_helper.rb
|
117
|
+
has_rdoc: true
|
118
|
+
homepage: http://github.com/squeejee/harvestr
|
119
|
+
licenses: []
|
120
|
+
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options:
|
123
|
+
- --charset=UTF-8
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: "0"
|
131
|
+
version:
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: "0"
|
137
|
+
version:
|
138
|
+
requirements: []
|
139
|
+
|
140
|
+
rubyforge_project: harvestr
|
141
|
+
rubygems_version: 1.3.5
|
142
|
+
signing_key:
|
143
|
+
specification_version: 3
|
144
|
+
summary: wrapper for the Harvest api
|
145
|
+
test_files:
|
146
|
+
- test/harvestr/base_test.rb
|
147
|
+
- test/harvestr/reports_test.rb
|
148
|
+
- test/harvestr/time_tracking_test.rb
|
149
|
+
- test/test_helper.rb
|
150
|
+
- examples/time_tracking.rb
|