freshtrack 0.2.0
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.txt +4 -0
- data/License.txt +20 -0
- data/Manifest.txt +37 -0
- data/README.txt +30 -0
- data/Rakefile +4 -0
- data/bin/freshtrack +23 -0
- data/config/hoe.rb +74 -0
- data/config/requirements.rb +17 -0
- data/lib/freshbooks/extensions/base_object.rb +31 -0
- data/lib/freshbooks/extensions/project.rb +58 -0
- data/lib/freshbooks/extensions/task.rb +50 -0
- data/lib/freshbooks/extensions/time_entry.rb +53 -0
- data/lib/freshbooks/extensions.rb +5 -0
- data/lib/freshtrack/core_ext/array.rb +11 -0
- data/lib/freshtrack/core_ext/numeric.rb +5 -0
- data/lib/freshtrack/core_ext/time.rb +3 -0
- data/lib/freshtrack/core_ext.rb +3 -0
- data/lib/freshtrack/version.rb +9 -0
- data/lib/freshtrack.rb +107 -0
- data/log/debug.log +0 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/setup.rb +1585 -0
- data/spec/array_spec.rb +35 -0
- data/spec/base_object_spec.rb +51 -0
- data/spec/freshtrack_spec.rb +566 -0
- data/spec/numeric_spec.rb +21 -0
- data/spec/project_spec.rb +400 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/task_spec.rb +350 -0
- data/spec/time_entry_spec.rb +351 -0
- data/spec/time_spec.rb +37 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/rspec.rake +21 -0
- data/tasks/website.rake +9 -0
- metadata +110 -0
data/History.txt
ADDED
data/License.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Yossef Mendelssohn
|
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/Manifest.txt
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
History.txt
|
2
|
+
License.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
bin/freshtrack
|
7
|
+
config/hoe.rb
|
8
|
+
config/requirements.rb
|
9
|
+
lib/freshtrack.rb
|
10
|
+
lib/freshtrack/version.rb
|
11
|
+
lib/freshtrack/core_ext.rb
|
12
|
+
lib/freshtrack/core_ext/array.rb
|
13
|
+
lib/freshtrack/core_ext/numeric.rb
|
14
|
+
lib/freshtrack/core_ext/time.rb
|
15
|
+
lib/freshbooks/extensions.rb
|
16
|
+
lib/freshbooks/extensions/base_object.rb
|
17
|
+
lib/freshbooks/extensions/project.rb
|
18
|
+
lib/freshbooks/extensions/task.rb
|
19
|
+
lib/freshbooks/extensions/time_entry.rb
|
20
|
+
log/debug.log
|
21
|
+
script/destroy
|
22
|
+
script/generate
|
23
|
+
setup.rb
|
24
|
+
spec/base_object_spec.rb
|
25
|
+
spec/freshtrack_spec.rb
|
26
|
+
spec/project_spec.rb
|
27
|
+
spec/task_spec.rb
|
28
|
+
spec/time_entry_spec.rb
|
29
|
+
spec/array_spec.rb
|
30
|
+
spec/numeric_spec.rb
|
31
|
+
spec/time_spec.rb
|
32
|
+
spec/spec.opts
|
33
|
+
spec/spec_helper.rb
|
34
|
+
tasks/deployment.rake
|
35
|
+
tasks/environment.rake
|
36
|
+
tasks/rspec.rake
|
37
|
+
tasks/website.rake
|
data/README.txt
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
Freshtrack is used to automatically create time entries in FreshBooks.
|
2
|
+
|
3
|
+
It presently depends on punch, the gem by Ara T. Howard, and any arguments given to
|
4
|
+
freshtrack are passed along to punch as if freshtrack were an alias for 'punch list'.
|
5
|
+
|
6
|
+
For example
|
7
|
+
|
8
|
+
freshtrack proj --after 2008-01-16 --before 2008-02-01
|
9
|
+
|
10
|
+
would get time data for the second half of January 2008 by using the command
|
11
|
+
|
12
|
+
punch list proj --after 2008-01-16 --before 2008-02-01
|
13
|
+
|
14
|
+
|
15
|
+
Freshtrack requires a configuration file, ~/.freshtrack.yml, that looks something like
|
16
|
+
|
17
|
+
---
|
18
|
+
company: Company Name
|
19
|
+
token: API Token
|
20
|
+
project_task_mapping:
|
21
|
+
project_name:
|
22
|
+
:project: FreshBooks Project Name
|
23
|
+
:task: FreshBooks Task Name
|
24
|
+
|
25
|
+
The 'Company Name' is the XXX in 'XXX.freshbooks.com'. The 'project_name' is the XXX in 'punch list XXX'
|
26
|
+
|
27
|
+
|
28
|
+
NOTE: As of this writing, punch (0.0.1) specifically requires attributes version 5.0.0 even though 5.0.1 is out.
|
29
|
+
Because of the way gems work, punch will not work if both are installed. Make sure the specific installed version
|
30
|
+
of attributes is 5.0.0.
|
data/Rakefile
ADDED
data/bin/freshtrack
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Created on 2008-1-30.
|
4
|
+
# Copyright (c) 2008. All rights reserved.
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'rubygems'
|
8
|
+
rescue LoadError
|
9
|
+
# no rubygems to load, so we fail silently
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'freshtrack'
|
13
|
+
|
14
|
+
# do stuff
|
15
|
+
project = ARGV.shift
|
16
|
+
|
17
|
+
unless project
|
18
|
+
puts "Usage: #{File.basename($0)} [project] [options]"
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
Freshtrack.init
|
23
|
+
Freshtrack.track(project, ARGV.join(' '))
|
data/config/hoe.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'freshtrack/version'
|
2
|
+
|
3
|
+
AUTHOR = 'Yossef Mendelssohn' # can also be an array of Authors
|
4
|
+
EMAIL = 'ymendel@pobox.com'
|
5
|
+
DESCRIPTION = "description of gem"
|
6
|
+
GEM_NAME = 'freshtrack' # what ppl will type to install your gem
|
7
|
+
RUBYFORGE_PROJECT = 'yomendel' # The unix name for your project
|
8
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
9
|
+
DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
10
|
+
|
11
|
+
@config_file = "~/.rubyforge/user-config.yml"
|
12
|
+
@config = nil
|
13
|
+
RUBYFORGE_USERNAME = "unknown"
|
14
|
+
def rubyforge_username
|
15
|
+
unless @config
|
16
|
+
begin
|
17
|
+
@config = YAML.load(File.read(File.expand_path(@config_file)))
|
18
|
+
rescue
|
19
|
+
puts <<-EOS
|
20
|
+
ERROR: No rubyforge config file found: #{@config_file}
|
21
|
+
Run 'rubyforge setup' to prepare your env for access to Rubyforge
|
22
|
+
- See http://newgem.rubyforge.org/rubyforge.html for more details
|
23
|
+
EOS
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
end
|
27
|
+
RUBYFORGE_USERNAME.replace @config["username"]
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
REV = nil
|
32
|
+
# UNCOMMENT IF REQUIRED:
|
33
|
+
# REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
|
34
|
+
VERS = Freshtrack::VERSION::STRING + (REV ? ".#{REV}" : "")
|
35
|
+
RDOC_OPTS = ['--quiet', '--title', 'freshtrack documentation',
|
36
|
+
"--opname", "index.html",
|
37
|
+
"--line-numbers",
|
38
|
+
"--main", "README",
|
39
|
+
"--inline-source"]
|
40
|
+
|
41
|
+
class Hoe
|
42
|
+
def extra_deps
|
43
|
+
@extra_deps.reject! { |x| Array(x).first == 'hoe' }
|
44
|
+
@extra_deps
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Generate all the Rake tasks
|
49
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
50
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
51
|
+
p.author = AUTHOR
|
52
|
+
p.description = DESCRIPTION
|
53
|
+
p.email = EMAIL
|
54
|
+
p.summary = DESCRIPTION
|
55
|
+
p.url = HOMEPATH
|
56
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
57
|
+
p.test_globs = ["test/**/test_*.rb"]
|
58
|
+
p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
|
59
|
+
|
60
|
+
# == Optional
|
61
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
62
|
+
p.extra_deps = [ # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
63
|
+
['freshbooks', '>= 1.0.0'],
|
64
|
+
['punch', '>= 0.0.1']
|
65
|
+
]
|
66
|
+
|
67
|
+
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
|
72
|
+
PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
|
73
|
+
hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
|
74
|
+
hoe.rsync_args = '-av --delete --ignore-errors'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
include FileUtils
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
%w[rake hoe newgem rubigen].each do |req_gem|
|
6
|
+
begin
|
7
|
+
require req_gem
|
8
|
+
rescue LoadError
|
9
|
+
puts "This Rakefile requires the '#{req_gem}' RubyGem."
|
10
|
+
puts "Installation: gem install #{req_gem} -y"
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
$:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
|
16
|
+
|
17
|
+
require 'freshtrack'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module FreshBooks
|
4
|
+
class BaseObject
|
5
|
+
MAPPING_FNS[Date] = lambda { |xml_val| Date.parse(xml_val.text) }
|
6
|
+
|
7
|
+
def to_xml
|
8
|
+
# The root element is the elem name
|
9
|
+
root = Element.new elem_name
|
10
|
+
|
11
|
+
# Add each BaseObject member to the root elem
|
12
|
+
self.members.each do |field_name|
|
13
|
+
|
14
|
+
value = self.send(field_name)
|
15
|
+
|
16
|
+
if value.is_a?(Array)
|
17
|
+
node = root.add_element(field_name)
|
18
|
+
value.each { |array_elem| node.add_element(array_elem.to_xml) }
|
19
|
+
elsif !value.nil?
|
20
|
+
root.add_element(field_name).text = value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
root
|
24
|
+
end
|
25
|
+
|
26
|
+
# The root element is the class name, downcased (and underscored if there is any CamelCase)
|
27
|
+
def elem_name
|
28
|
+
elem_name = self.class.to_s.split('::').last.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module FreshBooks
|
2
|
+
Project = BaseObject.new(:project_id, :name, :bill_method, :client_id, :rate, :description)
|
3
|
+
|
4
|
+
class Project
|
5
|
+
TYPE_MAPPINGS = { 'project_id' => Fixnum, 'client_id' => Fixnum, 'rate' => Float }
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def get(project_id)
|
9
|
+
resp = FreshBooks.call_api('project.get', 'project_id' => project_id)
|
10
|
+
return nil unless resp.success?
|
11
|
+
new_from_xml(resp.elements[1])
|
12
|
+
end
|
13
|
+
|
14
|
+
def list(options = {})
|
15
|
+
resp = FreshBooks.call_api('project.list', options)
|
16
|
+
return nil unless resp.success?
|
17
|
+
resp.elements.collect { |elem| new_from_xml(elem) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_by_name(name)
|
21
|
+
list.detect { |p| p.name == name }
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(project_id)
|
25
|
+
resp = FreshBooks.call_api('project.delete', 'project_id' => project_id)
|
26
|
+
resp.success?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def create
|
31
|
+
resp = FreshBooks.call_api('project.create', 'project' => self)
|
32
|
+
if resp.success?
|
33
|
+
self.project_id = resp.elements[1].text.to_i
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def update
|
38
|
+
resp = FreshBooks.call_api('project.update', 'project' => self)
|
39
|
+
resp.success?
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete
|
43
|
+
self.class.delete(project_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
def client
|
47
|
+
Client.get(client_id)
|
48
|
+
end
|
49
|
+
|
50
|
+
def tasks
|
51
|
+
Task.list('project_id' => project_id)
|
52
|
+
end
|
53
|
+
|
54
|
+
def time_entries
|
55
|
+
TimeEntry.list('project_id' => project_id)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module FreshBooks
|
2
|
+
Task = BaseObject.new(:task_id, :name, :billable, :rate, :description)
|
3
|
+
|
4
|
+
class Task
|
5
|
+
TYPE_MAPPINGS = { 'task_id' => Fixnum, 'rate' => Float }
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def get(task_id)
|
9
|
+
resp = FreshBooks.call_api('task.get', 'task_id' => task_id)
|
10
|
+
return nil unless resp.success?
|
11
|
+
new_from_xml(resp.elements[1])
|
12
|
+
end
|
13
|
+
|
14
|
+
def list(options = {})
|
15
|
+
resp = FreshBooks.call_api('task.list', options)
|
16
|
+
return nil unless resp.success?
|
17
|
+
resp.elements.collect { |elem| new_from_xml(elem) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_by_name(name)
|
21
|
+
list.detect { |p| p.name == name }
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(task_id)
|
25
|
+
resp = FreshBooks.call_api('task.delete', 'task_id' => task_id)
|
26
|
+
resp.success?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def create
|
31
|
+
resp = FreshBooks.call_api('task.create', 'task' => self)
|
32
|
+
if resp.success?
|
33
|
+
self.task_id = resp.elements[1].text.to_i
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def update
|
38
|
+
resp = FreshBooks.call_api('task.update', 'task' => self)
|
39
|
+
resp.success?
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete
|
43
|
+
self.class.delete(task_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
def time_entries
|
47
|
+
TimeEntry.list('task_id' => task_id)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module FreshBooks
|
2
|
+
TimeEntry = BaseObject.new(:time_entry_id, :project_id, :task_id, :hours, :date, :notes)
|
3
|
+
|
4
|
+
class TimeEntry
|
5
|
+
TYPE_MAPPINGS = {
|
6
|
+
'time_entry_id' => Fixnum, 'project_id' => Fixnum, 'task_id' => Fixnum,
|
7
|
+
'hours' => Float, 'date' => Date
|
8
|
+
}
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def get(time_entry_id)
|
12
|
+
resp = FreshBooks.call_api('time_entry.get', 'time_entry_id' => time_entry_id)
|
13
|
+
return nil unless resp.success?
|
14
|
+
new_from_xml(resp.elements[1])
|
15
|
+
end
|
16
|
+
|
17
|
+
def list(options = {})
|
18
|
+
resp = FreshBooks.call_api('time_entry.list', options)
|
19
|
+
return nil unless resp.success?
|
20
|
+
resp.elements.collect { |elem| new_from_xml(elem) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(time_entry_id)
|
24
|
+
resp = FreshBooks.call_api('time_entry.delete', 'time_entry_id' => time_entry_id)
|
25
|
+
resp.success?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def create
|
30
|
+
resp = FreshBooks.call_api('time_entry.create', 'time_entry' => self)
|
31
|
+
if resp.success?
|
32
|
+
self.time_entry_id = resp.elements[1].text.to_i
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def update
|
37
|
+
resp = FreshBooks.call_api('time_entry.update', 'time_entry' => self)
|
38
|
+
resp.success?
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete
|
42
|
+
self.class.delete(time_entry_id)
|
43
|
+
end
|
44
|
+
|
45
|
+
def task
|
46
|
+
Task.get(task_id)
|
47
|
+
end
|
48
|
+
|
49
|
+
def project
|
50
|
+
Project.get(project_id)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/freshtrack.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
require 'freshbooks/extensions'
|
3
|
+
require 'freshtrack/core_ext'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Freshtrack
|
7
|
+
class << self
|
8
|
+
attr_reader :config, :project, :task
|
9
|
+
|
10
|
+
def init
|
11
|
+
load_config
|
12
|
+
FreshBooks.setup("#{company}.freshbooks.com", token)
|
13
|
+
end
|
14
|
+
|
15
|
+
def load_config
|
16
|
+
@config = YAML.load(File.read(File.expand_path('~/.freshtrack.yml')))
|
17
|
+
end
|
18
|
+
|
19
|
+
def company
|
20
|
+
config['company']
|
21
|
+
end
|
22
|
+
|
23
|
+
def token
|
24
|
+
config['token']
|
25
|
+
end
|
26
|
+
|
27
|
+
def project_task_mapping
|
28
|
+
config['project_task_mapping']
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_project_data(project_name)
|
32
|
+
raise unless mapping = project_task_mapping[project_name]
|
33
|
+
@project = FreshBooks::Project.find_by_name(mapping[:project])
|
34
|
+
raise unless @project
|
35
|
+
@task = FreshBooks::Task.find_by_name(mapping[:task])
|
36
|
+
raise unless @task
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_time_data(project_name, options = '')
|
40
|
+
time_data = IO.read("| punch list #{project_name} #{options}")
|
41
|
+
convert_time_data(time_data)
|
42
|
+
end
|
43
|
+
|
44
|
+
def convert_time_data(time_data)
|
45
|
+
raw = YAML.load(time_data)
|
46
|
+
condense_time_data(raw)
|
47
|
+
end
|
48
|
+
|
49
|
+
def condense_time_data(time_data)
|
50
|
+
date_data = times_to_dates(time_data)
|
51
|
+
group_date_data(date_data)
|
52
|
+
end
|
53
|
+
|
54
|
+
def times_to_dates(time_data)
|
55
|
+
time_data.each do |td|
|
56
|
+
punch_in = td.delete('in')
|
57
|
+
punch_out = td.delete('out')
|
58
|
+
|
59
|
+
td['date'] = punch_in.to_date
|
60
|
+
td['hours'] = (punch_out - punch_in).secs_to_hours
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def group_date_data(date_data)
|
65
|
+
separator = '-' * 20
|
66
|
+
grouped = date_data.group_by { |x| x['date'] }
|
67
|
+
grouped.sort.inject([]) do |arr, (date, data)|
|
68
|
+
this_date = { 'date' => date }
|
69
|
+
this_date['hours'] = data.inject(0) { |sum, x| sum + x['hours'] }
|
70
|
+
this_date['hours'] = ('%.2f' % this_date['hours']).to_f
|
71
|
+
this_date['notes'] = data.collect { |x| x['log'].join("\n") }.join("\n" + separator + "\n")
|
72
|
+
arr + [this_date]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_data(project_name, options = '')
|
77
|
+
get_project_data(project_name)
|
78
|
+
get_time_data(project_name, options)
|
79
|
+
end
|
80
|
+
|
81
|
+
def track(project_name, options = '')
|
82
|
+
data = get_data(project_name, options)
|
83
|
+
data.each do |entry_data|
|
84
|
+
create_entry(entry_data)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def create_entry(entry_data)
|
89
|
+
time_entry = FreshBooks::TimeEntry.new
|
90
|
+
|
91
|
+
time_entry.project_id = project.project_id
|
92
|
+
time_entry.task_id = task.task_id
|
93
|
+
time_entry.date = entry_data['date']
|
94
|
+
time_entry.hours = entry_data['hours']
|
95
|
+
time_entry.notes = entry_data['notes']
|
96
|
+
|
97
|
+
result = time_entry.create
|
98
|
+
|
99
|
+
if result
|
100
|
+
true
|
101
|
+
else
|
102
|
+
STDERR.puts "warning: unsuccessful time entry creation for date #{entry_data['date']}"
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/log/debug.log
ADDED
File without changes
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.join(File.dirname(__FILE__), '..')
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.join(File.dirname(__FILE__), '..')
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|