toggler 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Koen Van der Auwera
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.
@@ -0,0 +1,17 @@
1
+ = toggl
2
+
3
+ Toggl API wrapper in a Ruby gem
4
+
5
+ https://www.toggl.com
6
+
7
+ https://www.toggl.com/public/api
8
+
9
+ == toggl command
10
+
11
+ If you want to create new tasks from the command line, make sure you create ~/.toggl file containing nothing but your API key.
12
+
13
+ run 'toggl' to see all options.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 Koen Van der Auwera. See LICENSE for details.
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+
4
+
5
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
6
+ require 'toggl'
7
+ require 'toggl_cmd/runner'
8
+ require 'toggl_cmd/runner_options'
9
+ require 'hirb'
10
+ require 'hirb/import_object'
11
+
12
+ Hirb.enable
13
+ TogglCmd::Runner.toggl(ARGV)
@@ -0,0 +1,113 @@
1
+ require "rubygems"
2
+ require "httparty"
3
+ require "chronic_duration"
4
+
5
+ class Toggl
6
+ include HTTParty
7
+ base_uri "https://toggl.com"
8
+ format :json
9
+
10
+ attr_reader :name, :api_token
11
+
12
+ def initialize(token, name="toggl-gem", debug=false)
13
+ self.class.default_params :output => 'json'
14
+ @api_token = token
15
+ @name = name
16
+ self.class.debug_output if debug
17
+ end
18
+
19
+ def delete_task(task_id)
20
+ delete 'tasks', task_id
21
+ end
22
+
23
+ def create_task(params={})
24
+ workspace = params[:workspace] || default_workspace_id
25
+ project_id = find_project_id(params[:project]) || create_project(params, workspace)
26
+ params[:billable] = true
27
+
28
+ params.merge!({ :created_with => name,
29
+ :workspace => {:id => workspace},
30
+ :project => {:id => project_id},
31
+ :tag_names => [name],
32
+ :start => start(params[:start]),
33
+ :duration => duration(params[:duration])})
34
+
35
+ post 'tasks', {:task => params}
36
+ end
37
+
38
+ def create_project(params={}, workspace=nil)
39
+ workspace ||= default_workspace_id
40
+ if project = post("projects",
41
+ :project => {:name => params[:project],
42
+ :workspace => {:id => workspace},
43
+ :billable => (params[:billable] || true)})
44
+ project["id"]
45
+ end
46
+ end
47
+
48
+ def default_workspace_id
49
+ self.workspaces.first["id"]
50
+ end
51
+
52
+ def find_project_id(str)
53
+ if project = self.projects.find{|project| project["client_project_name"].downcase =~ /#{str}/}
54
+ project["id"]
55
+ end
56
+ end
57
+
58
+ def duration(str)
59
+ str ? ChronicDuration.parse(str) : 1800
60
+ end
61
+
62
+ def start(value)
63
+ if value
64
+ case value
65
+ when "today"
66
+ Date.today
67
+ when "yesterday"
68
+ Date.today - 1
69
+ when "tomorrow"
70
+ Date.today + 1
71
+ else
72
+ DateTime.parse(value)
73
+ end
74
+ else
75
+ DateTime.now
76
+ end
77
+ end
78
+
79
+ def workspaces
80
+ get 'workspaces'
81
+ end
82
+
83
+ def tasks(params={})
84
+ get 'tasks', params
85
+ end
86
+
87
+ def projects
88
+ get 'projects'
89
+ end
90
+
91
+ def user_dump
92
+ get 'me', {:with_related_data => true}
93
+ end
94
+
95
+ private
96
+
97
+ def get(resource_name, data={})
98
+ self.class.get("/api/v3/#{resource_name}.json", :basic_auth => basic_auth, :query => data)
99
+ end
100
+
101
+ def post(resource_name, data)
102
+ self.class.post("/api/v3/#{resource_name}.json", :body => data, :basic_auth => basic_auth)
103
+ end
104
+
105
+ def delete(resource_name, id)
106
+ self.class.delete("/api/v3/#{resource_name}/#{id}.json", :basic_auth => basic_auth)
107
+ end
108
+
109
+ def basic_auth
110
+ {:username => self.api_token, :password => "api_token"}
111
+ end
112
+
113
+ end
@@ -0,0 +1,118 @@
1
+ require "optparse"
2
+ require "chronic_duration"
3
+ require 'yaml'
4
+ require 'active_record'
5
+
6
+ module TogglCmd
7
+
8
+ NAME = "toggl-gem"
9
+ PROJECT_FIELDS = %w(client name)
10
+ TASK_FIELDS = %w(id project description start duration billable)
11
+
12
+ class Runner
13
+
14
+ def self.toggl(args)
15
+ token = team_tokens.first[1]
16
+ options = RunnerOptions.new(args)
17
+ if options[:tasks]
18
+ prettify_tasks(Toggl.new(token, NAME).tasks)
19
+ elsif options[:projects]
20
+ prettify_projects(Toggl.new(token, NAME).projects)
21
+ elsif options[:delete]
22
+ toggl = Toggl.new(token, NAME)
23
+ toggl.delete_task(options[:delete])
24
+ prettify_tasks(toggl.tasks)
25
+ elsif options[:dump]
26
+ dump_tasks(options[:since], options[:through])
27
+ elsif options.any?
28
+ prettify_tasks(Toggl.new(token, NAME, options.delete(:debug)).create_task(options))
29
+ else
30
+ puts options.opts
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def self.prettify_tasks(values)
37
+ values = values["data"]
38
+ values = [values] unless values.is_a?(Array)
39
+ values.each do |value|
40
+ value["project"] = value["project"] ? value["project"]["name"] : ""
41
+ value["workspace"] = value["workspace"]["name"]
42
+ value["duration"] = ChronicDuration.output(value["duration"].to_i, :format => :short)
43
+ value["start"] = value["start"].strftime("%d/%m/%Y")
44
+ end
45
+ values.view(:class => :table, :fields => TASK_FIELDS)
46
+ end
47
+
48
+ def self.prettify_projects(values)
49
+ values = values["data"]
50
+ values = [values] unless values.is_a?(Array)
51
+ values.each do |value|
52
+ value["client"] = value["client"] ? value["client"]["name"] : ""
53
+ end
54
+
55
+ values.view(:class => :table, :fields => PROJECT_FIELDS)
56
+ end
57
+
58
+ def self.dump_tasks(since=nil, through=nil)
59
+ ActiveRecord::Base.establish_connection(config['database'])
60
+
61
+ team_tokens.each do |user, api_key|
62
+ tasks = TogglTask.load_tasks(user, Toggl.new(api_key), since, through)
63
+ puts "Dumping #{tasks.count} tasks from #{user} to the database"
64
+ tasks.each do |task|
65
+ task.save unless TogglTask.exists?(task.id) or task.duration < 0
66
+ end
67
+ end
68
+ end
69
+
70
+ def self.team_tokens
71
+ tokens = Hash.new
72
+ config["users"].each { |user, key| tokens[user] = key }
73
+ tokens
74
+ end
75
+
76
+ def self.config
77
+ YAML.load_file(File.expand_path("~/toggl_config.yaml"))
78
+ end
79
+
80
+ end
81
+
82
+ class TogglTask < ActiveRecord::Base
83
+ #attr_accessor :id, :start, :stop, :duration, :client, :project, :user, :billable, :description
84
+
85
+ def self.load_tasks(user, toggl, since, through)
86
+ client_projects = map_projects_to_clients(toggl.projects)
87
+ task_options = {:start_date => since || '1/1/2000', :end_date => through }
88
+
89
+ tasks = []
90
+ toggl.tasks(task_options)["data"].each do |t|
91
+ next if t["duration"] < 0
92
+ task = TogglTask.new(:start => t["start"],
93
+ :stop => t["stop"],
94
+ :duration => t["duration"],
95
+ :client => client_projects[t["project"]["id"]],
96
+ :project => t["project"]["name"],
97
+ :user => user,
98
+ :billable => t["billable"],
99
+ :description => t["description"])
100
+ task.id = t["id"]
101
+ tasks << task
102
+ end
103
+
104
+ tasks
105
+ end
106
+
107
+ def self.map_projects_to_clients(json_obj)
108
+ result = Hash.new
109
+
110
+ json_obj["data"].each do |project|
111
+ result[project["id"]] = project["client"] == nil ? '' : project["client"]["name"]
112
+ end
113
+
114
+ result
115
+ end
116
+ end
117
+
118
+ end
@@ -0,0 +1,71 @@
1
+ module TogglCmd
2
+
3
+ class RunnerOptions < Hash
4
+ attr_reader :opts
5
+
6
+ def initialize(args)
7
+ super()
8
+
9
+ @opts = OptionParser.new do |o|
10
+ o.banner = "Usage: #{File.basename($0)} [options]"
11
+
12
+ o.on('-D', '--dump', 'Dumps data for each user in ~/.toggl_team to a table in CRM.') do |dump|
13
+ self[:dump] = dump
14
+ end
15
+
16
+ o.on('-S', '--since DATE', 'The date (inclusive) to start dumping tasks from with the --dump option.') do |since|
17
+ self[:since] = since
18
+ end
19
+
20
+ o.on('-T', '--through DATE', 'The date (inclusive) to stop dumping tasks from with the --dump option.') do |through|
21
+ self[:through] = through
22
+ end
23
+
24
+ o.on('-t', '--title TITLE', 'What do you want to register?') do |title|
25
+ self[:description] = title
26
+ end
27
+
28
+ o.on('-p', '--project PROJECT', 'Who is going to pay?') do |project|
29
+ self[:project] = project
30
+ end
31
+
32
+ o.on('-s', '--duration DURATION', 'How long did it take?') do |duration|
33
+ self[:duration] = duration
34
+ end
35
+
36
+ o.on('-d', '--date DATE', 'When exactly did it happen?') do |date|
37
+ self[:start] = date
38
+ end
39
+
40
+ o.on('--tasks', 'Show tasks') do |tasks|
41
+ self[:tasks] = tasks
42
+ end
43
+
44
+ o.on('--projects', 'Show projects') do |projects|
45
+ self[:projects] = projects
46
+ end
47
+
48
+ o.on('--delete TASK_ID', 'Delete tasks with id') do |task_id|
49
+ self[:delete] = task_id
50
+ end
51
+
52
+ o.on('-v', '--verbose', 'What\'s happening?') do |debug|
53
+ self[:debug] = debug
54
+ end
55
+
56
+ o.on_tail('-h', '--help', 'Display this help and exit') do
57
+ puts @opts
58
+ exit
59
+ end
60
+
61
+ end
62
+
63
+ begin
64
+ @opts.parse!(args)
65
+ rescue OptionParser::InvalidOption => e
66
+ self[:invalid_argument] = e.message
67
+ end
68
+ end
69
+
70
+ end
71
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: toggler
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 4
9
+ version: 0.2.4
10
+ platform: ruby
11
+ authors:
12
+ - Caleb Harrelson
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-11-01 00:00:00 -05:00
18
+ default_executable: toggler
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: crack
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - "="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ - 1
31
+ - 8
32
+ version: 0.1.8
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: httparty
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - "="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ - 6
46
+ - 1
47
+ version: 0.6.1
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: chronic_duration
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 0
60
+ - 9
61
+ - 0
62
+ version: 0.9.0
63
+ type: :runtime
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: hirb
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 0
75
+ - 3
76
+ - 1
77
+ version: 0.3.1
78
+ type: :runtime
79
+ version_requirements: *id004
80
+ description: Toggler provides a simple REST-style JSON API over standard HTTP - http://www.toggl.com
81
+ email: charrelson@sincerasolutions.com
82
+ executables:
83
+ - toggler
84
+ extensions: []
85
+
86
+ extra_rdoc_files:
87
+ - LICENSE
88
+ - README.rdoc
89
+ files:
90
+ - lib/toggl.rb
91
+ - lib/toggl_cmd/runner.rb
92
+ - lib/toggl_cmd/runner_options.rb
93
+ - LICENSE
94
+ - README.rdoc
95
+ - bin/toggler
96
+ has_rdoc: true
97
+ homepage: http://github.com/atrophic/toggler
98
+ licenses: []
99
+
100
+ post_install_message:
101
+ rdoc_options:
102
+ - --charset=UTF-8
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ segments:
111
+ - 0
112
+ version: "0"
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ segments:
119
+ - 0
120
+ version: "0"
121
+ requirements: []
122
+
123
+ rubyforge_project: toggler
124
+ rubygems_version: 1.3.7
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: Toggl api ruby gem
128
+ test_files: []
129
+