slimtimercli 0.1.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.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.1.1 2008-04-03
2
+
3
+ * Initial Release of the gem
4
+ * started adding tests
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 FIXME full name
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,21 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ bin/slimtimer
7
+ config/hoe.rb
8
+ config/requirements.rb
9
+ lib/slimtimercli.rb
10
+ lib/slimtimercli/entities.rb
11
+ lib/slimtimercli/slim_timer.rb
12
+ lib/slimtimercli/version.rb
13
+ log/debug.log
14
+ setup.rb
15
+ spec/slimtimercli_spec.rb
16
+ spec/spec.opts
17
+ spec/spec_helper.rb
18
+ tasks/deployment.rake
19
+ tasks/environment.rake
20
+ tasks/rspec.rake
21
+ tasks/website.rake
data/README.txt ADDED
@@ -0,0 +1,37 @@
1
+ = slimtimercli
2
+
3
+ == DESCRIPTION:
4
+
5
+ SlimTimer is a tool to record your time spend on a
6
+ task. SlimTimer CLI allows you to controll your
7
+ SlimTimer directly from where you spend most of your
8
+ time - on the command line. To use SlimTimer proceed
9
+ with the following steps:
10
+
11
+ The first time you need to setup SlimTimer CLI with
12
+
13
+ slimtimer setup
14
+
15
+ Now it will ask for your email and password and API key
16
+ to use with your account. These information will be stored
17
+ in ~/.slimtimer/config.yml
18
+
19
+ To create a task run
20
+
21
+ slimtimer create_task my_shiny_task
22
+
23
+ To spend some time on the task you have to make the timer run
24
+
25
+ slimtimer start my_shiny_task
26
+
27
+ When you finished working on a task, you can call
28
+
29
+ slimtimer end
30
+
31
+ This will write the time spend back to SlimTimer.com.
32
+ Finally you can run
33
+
34
+ slimtimer tasks
35
+
36
+ To show all your tasks available.
37
+
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
data/bin/slimtimer ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+
4
+ gem 'slimtimercli'
5
+ require 'slimtimercli'
6
+
7
+ # Run
8
+ if ARGV.size == 0
9
+ Slimtimercli.__send__(:help)
10
+ else
11
+ Slimtimercli.__send__(ARGV[0].to_sym)
12
+ end
data/config/hoe.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'slimtimercli/version'
2
+
3
+ AUTHOR = 'Martin Grund' # can also be an array of Authors
4
+ EMAIL = "grundprinzip@gmail.com"
5
+ DESCRIPTION = "Command line interface to SlimTimer"
6
+ GEM_NAME = 'slimtimercli' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'rug-b' # The unix name for your project
8
+ HOMEPATH = "http://blog.grundprinzip.de"
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 = Slimtimercli::VERSION::STRING + (REV ? ".#{REV}" : "")
35
+ RDOC_OPTS = ['--quiet', '--title', 'slimtimercli 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.developer(AUTHOR, EMAIL)
52
+ p.description = DESCRIPTION
53
+ p.summary = DESCRIPTION
54
+ p.url = HOMEPATH
55
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
56
+ p.test_globs = ["test/**/test_*.rb"]
57
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
58
+
59
+ # == Optional
60
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
61
+ #p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
62
+
63
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
64
+
65
+ end
66
+
67
+ CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
68
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
69
+ $hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
70
+ $hoe.rsync_args = '-av --delete --ignore-errors'
@@ -0,0 +1,15 @@
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]))
@@ -0,0 +1,71 @@
1
+ class TimeEntry
2
+ attr_accessor :id, :start_time, :end_time,
3
+ :duration_in_seconds, :tags, :in_progress, :updated_at,
4
+ :created_at, :task
5
+
6
+ def self._load(hsh)
7
+ te = TimeEntry.new
8
+ hsh.each {|k,v|
9
+ te.__send__("#{k}=".to_sym, v) if te.respond_to?("#{k}=".to_sym)
10
+ }
11
+ end
12
+
13
+ def _serialize
14
+ {"time_entry" => {
15
+ "start_time" => @start_time,
16
+ "duration_in_seconds" => @duration_in_seconds,
17
+ "task_id" => @task.id}}
18
+ end
19
+
20
+ end
21
+
22
+ class Task
23
+ attr_accessor :name, :tags, :role, :owners, :hours,
24
+ :id
25
+
26
+ def self._load(hsh)
27
+ Task.new.__send__(:_load, hsh)
28
+ end
29
+
30
+ def _serialize
31
+ {"task" => instance_variables.map{ |i|
32
+ {i.to_s.gsub("@", "") => instance_variable_get(i)}
33
+ }.inject({}){|m,v| m.merge v}}
34
+ end
35
+
36
+ private
37
+
38
+ def _load(hsh)
39
+ hsh.each do |k,v|
40
+ self.instance_variable_set("@#{k}", v) if self.respond_to?(k.to_sym) &&
41
+ !v.kind_of?(Array)
42
+ end
43
+
44
+ @owners = hsh["owners"].map{|o| User._load(o)}
45
+ @coworkers = hsh["coworkers"].map{|o| User._load(o)}
46
+
47
+ self
48
+ end
49
+
50
+ end
51
+
52
+ class User
53
+ attr_accessor :email, :password, :user_id, :name
54
+
55
+ def initialize(e=nil, p=nil)
56
+ @email = e
57
+ @password = p
58
+ end
59
+
60
+ def self._load(hsh)
61
+ u = User.new
62
+ hsh.each do |k,v|
63
+ u.send("#{k}=".to_sym, v) if u.respond_to?("#{k}=".to_sym)
64
+ end
65
+ u
66
+ end
67
+
68
+ def _serialize
69
+ {"user" => {"email" => email, "password" => password}}
70
+ end
71
+ end
@@ -0,0 +1,139 @@
1
+ class SlimTimer
2
+
3
+ DATE_FORMAT = "%Y-%m-%d %H-%M-%S"
4
+
5
+ @@host = "slimtimer.com"
6
+ @@port = 80
7
+ #@@api_key = ""
8
+
9
+ attr_accessor :tasks, :time_entries
10
+
11
+ def initialize(user, pass, api)
12
+ @user = user; @pass = pass
13
+ @api_key = api
14
+ end
15
+
16
+ # Performs the login to the system, and stores
17
+ # the user id and the access token in the local
18
+ # class for reusse
19
+ def login
20
+ data = post_request("/users/token", User.new(@user, @pass)._serialize)
21
+ @token = data["access_token"]
22
+ @user_id = data["user_id"]
23
+ end
24
+
25
+ # Lists all tasks for the user logged in the system
26
+ # ==== Parameters
27
+ # show_completed<String>:: yes | no | only Include completed tasks (yes/no)
28
+ # or show only completed tasks Default: yes
29
+ # role<String>:: owner,coworker,reporter Include tasks where the user's role
30
+ # is one of the roles given (comma delimited) Default:
31
+ # owner,coworker
32
+ def tasks(show_completed = "yes", role="owner,coworker")
33
+ list = get_request("/users/#{@user_id}/tasks",
34
+ {"show_completed" => show_completed,
35
+ "role" => role})
36
+
37
+ list.map{ |t|
38
+ Task._load(t)
39
+ }
40
+
41
+ end
42
+
43
+ # Create a new task for this user
44
+ # ==== Parameters
45
+ # name<String>:: name for the new task
46
+ # tags<String>:: comma separated list of tags for the task
47
+ def create_task(name, tags= "", coworker_emails = "", reporter_emails = "")
48
+
49
+ t = Task.new
50
+ t.name = name
51
+ t.tags = tags
52
+
53
+ t._serialize
54
+
55
+
56
+ Task._load(post_request("/users/#{@user_id}/tasks", t._serialize))
57
+ end
58
+
59
+ def find_task_by_name(name)
60
+ tasks("no").find {|t| t.name == name}
61
+ end
62
+
63
+ def delete_task(name)
64
+ t = find_task_by_name(name)
65
+ delete_request("/users/#{@user_id}/tasks/#{t.id}")
66
+ end
67
+
68
+ # List all time entries for the user logged in
69
+ # ==== Parameters
70
+ # range_start<Time>:: start of the range
71
+ # range_end<Time>:: end of the range
72
+ def time_entries(range_start = nil, range_end = nil)
73
+ options = {}
74
+
75
+ options = {"range_start" =>
76
+ range_start.strftime(DATE_FORMAT)} if range_start
77
+ options = {"range_end" =>
78
+ range_end.strftime(DATE_FORMAT)} if range_end
79
+
80
+ # do the actual request
81
+ get_request("/users/#{@user_id}/time_entries", options)
82
+ end
83
+
84
+ def create_time_entry(task, start_time = Time.now, duration = 0)
85
+ te = TimeEntry.new
86
+ te.task = task; te.start_time = start_time
87
+ te.duration_in_seconds = duration
88
+
89
+ post_request("/users/#{@user_id}/time_entries", te._serialize)
90
+ end
91
+
92
+ protected
93
+
94
+ def handle_error(object)
95
+ if object.kind_of?(ActiveRecord::Errors)
96
+ raise "ActiveRecord::Errors " + object.map{|k,v| k + " " + v}.join("\n")
97
+ else
98
+ object
99
+ end
100
+ end
101
+
102
+ def get_request(path, params = {})
103
+ post_request(path, {"_method" => "get"}.merge(params))
104
+ end
105
+
106
+ def put_request(path, params = {})
107
+ post_request(path, {"_method" => "put"}.merge(params))
108
+ end
109
+
110
+ def delete_request(path, params = {})
111
+ post_request(path, {"_method" => "delete"}.merge(params))
112
+ end
113
+
114
+ def post_request(path, params = {})
115
+ request(Net::HTTP::Post.new(path, default_header), params)
116
+ end
117
+
118
+ def request(method, params = {})
119
+
120
+ puts "Start Request" if $DEBUG
121
+ # merge api key
122
+ params = {"api_key" => @api_key}.merge(params)
123
+ # If token there merge it
124
+ params = {"access_token" => @token}.merge(params) if @token
125
+ res, body = Net::HTTP.start(@@host,@@port) {|http|
126
+ p params if $DEBUG
127
+ method.body = params.to_yaml
128
+ http.request(method)
129
+ }
130
+ puts "Finished Request" if $DEBUG
131
+ handle_error(YAML.load(body))
132
+ end
133
+
134
+ def default_header
135
+ {"Accept" => "application/x-yaml",
136
+ "Content-Type" => "application/x-yaml"}
137
+ end
138
+
139
+ end
@@ -0,0 +1,9 @@
1
+ module Slimtimercli #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 1
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,215 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'fileutils'
5
+ require 'net/http'
6
+ require 'rubygems'
7
+ require 'active_record'
8
+ require 'active_support'
9
+ require 'yaml'
10
+
11
+ require "slimtimercli/entities"
12
+ require "slimtimercli/slim_timer"
13
+ require "slimtimercli/version"
14
+
15
+ module Slimtimercli
16
+ module Helper
17
+ def self.login
18
+ config = Helper::load_config
19
+ st = SlimTimer.new(config["email"], config["password"],
20
+ config["api_key"])
21
+ st.login
22
+
23
+ st
24
+ end
25
+
26
+ def self.root
27
+ File.join(ENV["HOME"], ".slimtimer")
28
+ end
29
+
30
+ def self.config_file
31
+ File.join(root, "config.yml")
32
+ end
33
+
34
+ def self.tasks_file
35
+ File.join(root, "tasks.yml")
36
+ end
37
+
38
+ def self.current_file
39
+ File.join(root, "current.yml")
40
+ end
41
+
42
+ def self.check_and_create_dir
43
+ raise "Home DIR not set!" unless ENV["HOME"]
44
+
45
+ unless File.directory?(root)
46
+ FileUtils.mkdir(root)
47
+ end
48
+ end
49
+
50
+ def self.load_config
51
+ check_and_create_dir
52
+
53
+ unless File.exists?(File.join(root, "config.yml"))
54
+ File.open( File.join(root, "config.yml"), 'w' ) do |out|
55
+ YAML.dump({}, out )
56
+ end
57
+ end
58
+ load_file("config.yml")
59
+ end
60
+
61
+ def self.save_config(config)
62
+ dump_to_file(config, "config.yml")
63
+ end
64
+
65
+ def self.load_file(file)
66
+ File.open( File.join(root, file) ) { |yf| YAML::load( yf ) }
67
+ end
68
+
69
+ def self.dump_to_file(object, file)
70
+ check_and_create_dir
71
+ File.open( File.join(root, file), 'w' ) do |out|
72
+ YAML.dump(object, out )
73
+ end
74
+ end
75
+ end
76
+
77
+ def self.create_task
78
+ name = ARGV[1]
79
+
80
+ st = Helper::login
81
+ if st.create_task(name)
82
+ Helper::dump_to_file(st.tasks, "tasks.yml")
83
+ puts "Task #{name} successfully created."
84
+ end
85
+ end
86
+
87
+ def self.tasks(show= true)
88
+ config = Helper::load_config
89
+ st = SlimTimer.new(config["email"], config["password"],
90
+ config["api_key"])
91
+
92
+ if !File.exists?(Helper::tasks_file) ||
93
+ File.mtime(Helper::tasks_file) < (Time.now - 60 * 60 *24)
94
+
95
+ st.login
96
+ Helper::dump_to_file(st.tasks, "tasks.yml")
97
+ end
98
+
99
+ tasks = Helper::load_file("tasks.yml")
100
+
101
+ return tasks unless show
102
+
103
+ tasks.each do |t|
104
+ puts t.name
105
+ end
106
+ end
107
+
108
+ def self.force_reload
109
+ config = Helper::load_config
110
+ st = SlimTimer.new(config["email"], config["password"],
111
+ config["api_key"])
112
+
113
+ st.login
114
+ Helper::dump_to_file(st.tasks, "tasks.yml")
115
+ tasks = Helper::load_file("tasks.yml")
116
+
117
+ tasks.each do |t|
118
+ puts t.name
119
+ end
120
+ end
121
+
122
+ # This method stores the credentials in the necessary directoyr
123
+ def self.setup
124
+ config = Helper::load_config
125
+
126
+ puts "Slimtimer Login Credentials\n"
127
+ print "E-Mail: "
128
+ config["email"] = STDIN.gets.gsub("\n", "")
129
+
130
+ print "Password: "
131
+ config["password"] = STDIN.gets.gsub("\n", "")
132
+
133
+ print "API Key: "
134
+ config["api_key"] = STDIN.gets.gsub("\n", "")
135
+
136
+ Helper::save_config(config)
137
+
138
+ # clear the screen
139
+ system("clear")
140
+ end
141
+
142
+ def self.help
143
+ puts <<-HELP
144
+ SlimTimer is a tool to record your time spend on a
145
+ task. SlimTimer CLI allows you to controll your
146
+ SlimTimer directly from where you spend most of your
147
+ time - on the command line. To use SlimTimer proceed
148
+ with the following steps:
149
+
150
+ The first time you need to setup SlimTimer CLI with
151
+
152
+ slimtimer setup
153
+
154
+ Now it will ask for your email and password and API key
155
+ to use with your account. These information will be stored
156
+ in ~/.slimtimer/config.yml
157
+
158
+ To create a task run
159
+
160
+ slimtimer create_task my_shiny_task
161
+
162
+ To spend some time on the task you have to make the timer run
163
+
164
+ slimtimer start my_shiny_task
165
+
166
+ When you finished working on a task, you can call
167
+
168
+ slimtimer end
169
+
170
+ This will write the time spend back to SlimTimer.com.
171
+ Finally you can run
172
+
173
+ slimtimer tasks
174
+
175
+ To show all your tasks available.
176
+ HELP
177
+ end
178
+
179
+ def self.start
180
+ info = {"task" => ARGV[1],
181
+ "start_time" => Time.now}
182
+
183
+ # dum curent task to file
184
+ Helper::dump_to_file(info, "current.yml")
185
+ end
186
+
187
+ def self.end
188
+ begin
189
+ info = Helper::load_file("current.yml")
190
+ rescue
191
+ puts "You must start a task before you finish it"
192
+ return
193
+ end
194
+
195
+
196
+ #Find task in tasks yml
197
+ t = tasks(false).find {|t| t.name == info["task"]}
198
+
199
+ raise "Task not found in list. Reload List?" unless t
200
+
201
+ st = Helper::login
202
+ result = st.create_time_entry(t, info["start_time"],
203
+ (Time.now - info["start_time"]).to_i)
204
+
205
+ # Delete yml file
206
+ if result
207
+ FileUtils.rm(Helper::current_file)
208
+ end
209
+
210
+ # Output
211
+ puts "Wrote new Entry for #{t.name}, duration #{result["duration_in_seconds"] / 60}m"
212
+
213
+ end
214
+
215
+ end
data/log/debug.log ADDED
File without changes