mite.cmd 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ pkg
3
+ doc
4
+ coverage
5
+ .DS_Store
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Overbryd
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,162 @@
1
+ <pre>
2
+
3
+ _ _
4
+ (_) | | |
5
+ _ __ ___ _| |_ ___ ___ _ __ ___ __| |
6
+ | '_ ` _ \| | __/ _ \ / __| '_ ` _ \ / _` |
7
+ | | | | | | | || __/| (__| | | | | | (_| |
8
+ |_| |_| |_|_|\__\___(_)___|_| |_| |_|\__,_|
9
+
10
+ </pre>
11
+ A simple command line interface for basic mite tasks.
12
+
13
+ * @mite.@ is an ingeniously sleek time tracking tool: http://mite.yo.lk
14
+ * @mite.cmd@ is a command line interface for it: http://github.com/Overbryd/mite.cmd
15
+ It provides a system wide command called @mite@.
16
+
17
+ h3. Installation instructions:
18
+
19
+ $ gem install Overbryd-mite.cmd
20
+
21
+ I assume github is in your gem sources. You may need @activesupport@ and @activeresource@ too.
22
+
23
+ $ gem install activesupport activeresource
24
+
25
+ h3. After installation instructions:
26
+
27
+ You'll need to configure the client prior using it.
28
+ To do this, you can just hammer into your console:
29
+
30
+ $ mite configure "Your Account Name" "Your API Key"
31
+
32
+ It will then generate a yml file in ~/.mite.yml with your account information.
33
+
34
+ The configure command also tries to install the bash auto completion hook into the following files: @~/.bash_completion@, @~/.bash_profile@, @~/.bash_login@, @~/.bashrc@.
35
+
36
+ If your system doesn't support one of the files above, install the hook by yourself.
37
+ In order to work as expected it needs bash to be configured for auto completion.
38
+ This is actually quite easy, just append this line to your bash config file.
39
+
40
+ complete -C "mite auto-complete" mite
41
+
42
+ You could use this command as an example (replace .bash_login with your bash configuration file):
43
+
44
+ $ echo "complete -C \"mite auto-complete\" mite" >> ~/.bash_login
45
+
46
+ h3. Create new time entries with ease:
47
+
48
+ $ mite "project name" "service name" "start time" "note"
49
+
50
+ $ mite "project name" "service name" "note"
51
+
52
+ $ mite "project name" "start time" "note"
53
+
54
+ $ mite "start time" "note"
55
+
56
+ h4. The magic of the moment err... start_time:
57
+
58
+ Append a @+@ to the start time and it will start the tracker.
59
+
60
+ The most convenient syntax to set the start time is @h:mm@. So it will accept all these kinds of values: @1:15@, @0:01@, @24:00@.
61
+
62
+ Short-hand for hours:
63
+ * @3+@ will set the start time to 3 hours, and start the tracker
64
+ * @3.5@ will set it to 3 hours 30 minutes
65
+ * @0.25+@ will make you look like a floating-point fetish, and start the tracker
66
+
67
+ h4. Examples:
68
+
69
+ $ mite "World Domination"
70
+
71
+ This will start a timer for the project World Domination.
72
+
73
+ $ mite HugEveryone 0:15
74
+
75
+ The time entry created by this command, is made for the project HugEveryone and is set to 15 minutes. No tracker will be started.
76
+
77
+ $ mite HugEveryone Love 1+
78
+
79
+ Start a time entry for the project HugEveryone with the service Love and set it to 1 hour. The tracker will be started, because of the @start time@ argument was suffixed with a @+@.
80
+
81
+ h4. Note:
82
+
83
+ If a project or a service doesn't exist it will be created.
84
+ You can omit one/some of the arguments, namely: @project name@, @service name@, @note@ or @start time@. But they have to be in order. So you can't set a @service name@ unless you specify a project. This will be fixed in future releases, till then, the arguments rely on their order.
85
+
86
+ h3. Amazing auto-completion:
87
+
88
+ This is very nifty. (But maybe you should read the After Installation Instructions before.)
89
+ The client was designed to save keystrokes, so I've baked in a very handy auto-completion feature. It even ignores the typed case.
90
+
91
+ Try this:
92
+
93
+ $ mite [tab]
94
+
95
+ It will try to auto-complete your projects.
96
+
97
+ $ mite Project1 [tab]
98
+
99
+ It will try to auto-complete your services.
100
+
101
+ $ mite Project1 "System Administration" [tab]
102
+
103
+ It will try to auto-complete common times!
104
+
105
+ $ mite Project1 "System Administration" 0:48 [tab]
106
+
107
+ It will try to auto-complete your notes!
108
+
109
+ h4. Amazingly fast auto-completion:
110
+
111
+ The auto-completion feature creates a cache in ~/.mite.cache, if you want to rebuild this cache just hit:
112
+
113
+ $ mite rebuild-cache
114
+
115
+ h4. Note: The first run without the cache might be a bit slow.
116
+
117
+ h3. Controlling timers:
118
+
119
+ $ mite start
120
+
121
+ This little cutey will start today's last time entry, if there is one.
122
+
123
+ $ mite stop
124
+
125
+ This will just stop the current timer. (If you like you can use `mite pause` or `mite lunch` too)
126
+
127
+ h3. Simple reports:
128
+
129
+ $ mite today
130
+
131
+ This will generate a report of today's activity, summarizing your earnings at the bottom.
132
+
133
+ $ mite yesterday
134
+
135
+ This will generate a report of yesterday's activity, summarizing your earnings and the total time at the bottom.
136
+ Also works using @this_week@, @last_week@, @this_month@, @last_month@ as argument.
137
+
138
+ h3. More simple stuff:
139
+
140
+ $ mite
141
+
142
+ If there is a running timer, it will output it. Otherwise you should better not listen to it.
143
+
144
+ $ mite +
145
+
146
+ Just create a new time entry and start tracking it.
147
+
148
+ $ mite open
149
+
150
+ Opens your mite account in a new browser window (Max OSX only) or prints the url to your account.
151
+
152
+ h3. Feature Request? Problems? Found a bug? Your girlfriend broke up with you?
153
+
154
+ Your problems are no longer "problems"! They are called issues now, and github has the solution.
155
+
156
+ If you want to tell me an idea, I'll happily take care of them in the issues tracker: http://github.com/Overbryd/mite.cmd/issues
157
+
158
+ |\3. **All time Highscore of people who were involved** |
159
+ | who? |\2. what? |
160
+ | Sebastian Munz (yolk) | 12 issues | found a lot of bugs, did some real use testing. Really cool, thank you! |
161
+ | Julia Soergel (juliasoergel) | 2 issues | found a bug and `I like your hairstyle!´ |
162
+ | Michael Bumann (burmi) | 2 issues | found two bugs and mentioned a security issue which is now fixed. Yea baby! |
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "mite.cmd"
8
+ gem.executables = "mite"
9
+ gem.summary = "A simple command line interface for basic mite tasks."
10
+ gem.email = "l.rieder@gmail.com"
11
+ gem.homepage = "http://github.com/Overbryd/mite.cmd"
12
+ gem.description = "A simple command line interface for mite, a sleek time tracking webapp."
13
+ gem.authors = ["Lukas Rieder"]
14
+ end
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
17
+ end
18
+
19
+ task :spec do
20
+ spec_files = Dir.glob('spec/**/*_spec.rb').join(' ')
21
+ sh "spec #{spec_files} --format specdoc --color"
22
+ end
23
+ task :default => :spec
24
+
25
+ task :rcov do
26
+ sh "rake run_rcov && open coverage/index.html"
27
+ end
28
+ require 'spec/rake/spectask'
29
+ Spec::Rake::SpecTask.new(:run_rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+ * Make it soft around the edges
2
+ * Improve auto-completion caching
3
+ * Waiting for some feedback
4
+ * Implementing feedback
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.10
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'mite_cmd'
6
+
7
+ begin
8
+ MiteCmd.calling_script = __FILE__
9
+ MiteCmd.run(ARGV)
10
+ rescue MiteCmd::Exception => e
11
+ puts "#{"Epic fail: #{e.message}".colorize(:color => :red, :background => :black)} Type `mite help` to get help."
12
+ exit(1)
13
+ end
@@ -0,0 +1,34 @@
1
+ require 'yaml'
2
+ Dir[File.join(File.dirname(__FILE__), *%w[.. vendor * lib])].each do |path|
3
+ $LOAD_PATH.unshift path
4
+ end
5
+ require 'mite-rb'
6
+
7
+ require 'string_ext'
8
+ require 'mite_ext'
9
+ require 'mite_cmd/application'
10
+ require 'mite_cmd/autocomplete'
11
+
12
+ module MiteCmd
13
+ BASH_COMPLETION = "complete -C \"mite auto-complete\" mite"
14
+
15
+ CONFIG_FILE = File.expand_path '~/.mite.yml'
16
+
17
+ mattr_accessor :calling_script
18
+
19
+ def self.load_configuration
20
+ if File.exist?(CONFIG_FILE)
21
+ configuration = YAML.load(File.read(CONFIG_FILE))
22
+ Mite.account = configuration[:account]
23
+ Mite.key = configuration[:apikey]
24
+ else
25
+ raise Exception.new("Configuration file is missing.")
26
+ end
27
+ end
28
+
29
+ def self.run(args)
30
+ Application.new(args).run
31
+ end
32
+
33
+ class Exception < StandardError; end
34
+ end
@@ -0,0 +1,180 @@
1
+ require 'ftools'
2
+
3
+ module MiteCmd
4
+ class Application
5
+ TIME_FORMAT = /^(\d+(\.\d+)?:?\+?)|(\d+:\d+\+?)|\+$/
6
+ FLIRTS = [
7
+ 'I like your hairstyle.', 'What a nice console you have.', 'My favorite color is red on black, monospaced.',
8
+ "What a lovely operation system this #{`uname`} is.", 'What about dinner tonight?', 'Your keystrokes are tingling.'
9
+ ]
10
+
11
+ def initialize(arguments=[])
12
+ @arguments = arguments
13
+ MiteCmd.load_configuration unless ['configure', 'help'].include?(arguments.first)
14
+ end
15
+
16
+ def run
17
+ if @arguments.first == 'open'
18
+ open_or_echo Mite.account_url
19
+
20
+ elsif @arguments.first == 'help'
21
+ open_or_echo 'http://github.com/Overbryd/mite.cmd'
22
+
23
+ elsif @arguments.first == 'configure'
24
+ raise MiteCmd::Exception.new('mite configure needs two arguments, the account name and the apikey') if @arguments.size < 3 # lol boobs, err... an ice cone!
25
+ write_configuration({:account => @arguments[1], :apikey => @arguments[2]})
26
+ tell("Couldn't set up bash completion. I'm terribly frustrated. Maybe 'mite help' helps out.") unless try_to_setup_bash_completion
27
+
28
+ elsif @arguments.first == 'auto-complete'
29
+ autocomplete = MiteCmd::Autocomplete.new(MiteCmd.calling_script)
30
+ autocomplete.completion_table = if File.exist?(cache_file)
31
+ Marshal.load File.read(cache_file)
32
+ else
33
+ rebuild_completion_table
34
+ end
35
+ autocomplete.suggestions.map(&:quote_if_spaced).each { |s| tell s }
36
+
37
+ elsif @arguments.first == 'rebuild-cache'
38
+ File.delete(cache_file) if File.exist? cache_file
39
+ rebuild_completion_table
40
+ tell 'The rebuilding of the cache has been done, Master. Your wish is my command.'
41
+
42
+ elsif ['today', 'yesterday', 'this_week', 'last_week', 'this_month', 'last_month'].include? @arguments.first
43
+ total_minutes = 0
44
+ total_revenue = Mite::TimeEntry.all(:params => {:at => @arguments.first, :user_id => 'current'}).each do |time_entry|
45
+ total_minutes += time_entry.minutes
46
+ tell time_entry.inspect
47
+ end.map(&:revenue).compact.sum
48
+ tell ("%s:%.2d" % [total_minutes/60, total_minutes-total_minutes/60*60]).colorize(:lightred) + ", " + ("%.2f $" % (total_revenue/100)).colorize(:lightgreen)
49
+
50
+ elsif ['stop', 'pause', 'lunch'].include? @arguments.first
51
+ if current_tracker = (Mite::Tracker.current ? Mite::Tracker.current.stop : nil)
52
+ tell current_tracker.time_entry.inspect
53
+ end
54
+
55
+ elsif @arguments.first == 'start'
56
+ if time_entry = Mite::TimeEntry.first(:params => {:at => 'today'})
57
+ time_entry.start_tracker
58
+ tell time_entry.inspect
59
+ else
60
+ tell "Oh my dear! I tried hard, but I could'nt find any time entry for today."
61
+ end
62
+
63
+ elsif (1..4).include?(@arguments.size)
64
+ attributes = {}
65
+ if time_string = @arguments.select { |a| a =~ TIME_FORMAT }.first
66
+ attributes[:minutes] = parse_minutes(time_string)
67
+ start_tracker = (time_string =~ /\+$/)
68
+ end
69
+ if project = find_or_create_project(@arguments.first)
70
+ attributes[:project_id] = project.id
71
+ end
72
+ if @arguments[1] && service = find_or_create_service(@arguments[1])
73
+ attributes[:service_id] = service.id
74
+ end
75
+ if note = parse_note(@arguments, time_string)
76
+ attributes[:note] = note
77
+ end
78
+ time_entry = Mite::TimeEntry.create attributes
79
+ time_entry.start_tracker if start_tracker
80
+ tell time_entry.inspect
81
+
82
+ elsif @arguments.size == 0
83
+ tell Mite::Tracker.current ? Mite::Tracker.current.inspect : flirt
84
+ end
85
+ end
86
+
87
+ def say(what)
88
+ puts what
89
+ end
90
+ alias_method :tell, :say
91
+
92
+ def flirt
93
+ FLIRTS[rand(FLIRTS.size)]
94
+ end
95
+
96
+ private
97
+
98
+ def find_or_create_project(name)
99
+ project = Mite::Project.first(:params => {:name => name})
100
+ return nil if name =~ TIME_FORMAT
101
+ project ? project : Mite::Project.create(:name => name)
102
+ end
103
+
104
+ def find_or_create_service(name)
105
+ service = Mite::Service.first(:params => {:name => name})
106
+ return nil if name =~ TIME_FORMAT
107
+ service ? service : Mite::Service.create(:name => name)
108
+ end
109
+
110
+ def parse_note(args, time_string)
111
+ if args[3]
112
+ args[3]
113
+ elsif time_string.nil? && args[2]
114
+ args[2]
115
+ elsif time_string && args[args.index(time_string)+1]
116
+ args[args.index(time_string)+1]
117
+ else
118
+ nil
119
+ end
120
+ end
121
+
122
+ def parse_minutes(string)
123
+ string = string.sub(/\+$/, '')
124
+
125
+ if string.blank?
126
+ 0
127
+ elsif string =~ /^\d+:\d+$/
128
+ string.split(':').first.to_i*60 + string.split(':').last.to_i
129
+ elsif string =~ /^\d+(\.\d+)?:?$/
130
+ (string.to_f*60).to_i
131
+ end
132
+ end
133
+
134
+ def rebuild_completion_table
135
+ completion_table = {
136
+ 0 => Mite::Project.all.map(&:name),
137
+ 1 => Mite::Service.all.map(&:name),
138
+ 2 => ['0:05', '0:05+', '0:15', '0:15+', '0:30', '0:30+', '1:00', '1:00+'].map(&:quote),
139
+ 3 => Mite::TimeEntry.all.map(&:note).compact
140
+ }
141
+ File.open(cache_file, 'w') { |f| Marshal.dump(completion_table, f) }
142
+ File.chmod(0600, cache_file)
143
+ completion_table
144
+ end
145
+
146
+ def cache_file
147
+ File.expand_path('~/.mite.cache')
148
+ end
149
+
150
+ def open_or_echo(open_argument)
151
+ exec "open '#{open_argument}' || echo '#{open_argument}'"
152
+ end
153
+
154
+ def write_configuration(config)
155
+ File.open(File.expand_path('~/.mite.yml'), 'w') do |f|
156
+ YAML.dump(config, f)
157
+ end
158
+ File.chmod(0600, File.expand_path('~/.mite.yml'))
159
+ end
160
+
161
+ def try_to_setup_bash_completion
162
+ bash_code = "\n\n#{MiteCmd::BASH_COMPLETION}"
163
+
164
+ ['~/.bash_completion', '~/.bash_profile', '~/.bash_login', '~/.bashrc'].each do |file|
165
+ bash_config_file = File.expand_path file
166
+ next unless File.file?(bash_config_file)
167
+ unless File.read(bash_config_file) =~ /#{bash_code}/
168
+ File.open(bash_config_file, 'a') do |f|
169
+ f.puts bash_code
170
+ end
171
+ return true
172
+ else
173
+ return true
174
+ end
175
+ end
176
+ return false
177
+ end
178
+
179
+ end
180
+ end