achoo 0.3

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.
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rake/gempackagetask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/testtask'
6
+ require 'rubygems'
7
+
8
+ load File.dirname(__FILE__) + '/achoo.gemspec'
9
+
10
+ task :default => ['test:unit']
11
+
12
+ namespace 'build' do
13
+
14
+ Rake::GemPackageTask.new(Spec) do |pkg|
15
+ pkg.need_tar = true
16
+ end
17
+
18
+ end
19
+
20
+ Rake::TestTask.new do |t|
21
+ t.libs << "test/lib"
22
+ t.test_files = FileList['test/**/test*.rb']
23
+ #t.verbose = true
24
+ end
25
+
26
+ namespace 'test' do
27
+
28
+ Rake::TestTask.new do |t|
29
+ t.name = :unit
30
+ t.libs << "test/lib"
31
+ t.test_files = FileList['test/unit/test*.rb']
32
+ end
33
+
34
+ Rake::TestTask.new do |t|
35
+ t.name = :acceptance
36
+ t.libs << "test/lib"
37
+ t.test_files = FileList['test/acceptance/test*.rb']
38
+ end
39
+
40
+ desc 'Measures test coverage'
41
+ task :coverage do
42
+ rm_f('coverage')
43
+ system("rcov -T -Ilib test/unit/test_*.rb")
44
+ end
45
+
46
+ end
47
+
48
+ namespace 'doc' do
49
+
50
+ Rake::RDocTask.new do |rd|
51
+ rd.title = 'Achoo --- The Achievo CLI'
52
+ rd.main = "README.rdoc"
53
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
54
+ rd.template = `allison --path`.chop + '.rb'
55
+ rd.options << '--line-numbers' << '--inline-source'
56
+ rd.rdoc_dir = 'doc'
57
+ end
58
+
59
+ end
60
+
61
+ desc 'Install development dependencies'
62
+ task :setup do
63
+ system 'gem install shoulda rack thin redgreen allison'
64
+ end
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'achoo'
4
+ require 'achoo/rc_loader'
5
+ require 'optparse'
6
+
7
+ include Achoo::RCLoader
8
+
9
+ def main
10
+ trap("INT") { exit }
11
+
12
+ abort "UTF-8 terminal required" unless utf8?
13
+
14
+ options = {
15
+ :log => false,
16
+ :rc_file => "#{ENV['HOME']}/.achoo",
17
+ }.merge(parse_args)
18
+
19
+ load_rc(options[:rc_file])
20
+
21
+ achoo = Achoo.new(options[:log])
22
+ achoo.start
23
+ end
24
+
25
+ def parse_args
26
+ options = {}
27
+
28
+ optparse = OptionParser.new do|opts|
29
+ opts.banner = "Usage: $0 [options]"
30
+
31
+ opts.on( '-l', '--log', 'Write http communication to achoo_http.log' ) do
32
+ options[:log] = true
33
+ end
34
+
35
+ opts.on('--rcfile FILE', 'FIX' ) do |file|
36
+ options[:rc_file] = file
37
+ end
38
+ end
39
+
40
+ optparse.parse!
41
+ options
42
+ end
43
+
44
+ def utf8?
45
+ `locale charmap`.chop == 'UTF-8'
46
+ end
47
+
48
+ main
@@ -0,0 +1,31 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'achoo/awake'
4
+ require 'date'
5
+ require 'optparse'
6
+
7
+ def main
8
+ awake = Achoo::Awake.new
9
+
10
+ options = {
11
+ :all => false,
12
+ }
13
+
14
+ optparse = OptionParser.new do|opts|
15
+ opts.banner = "Usage: $0 [options]"
16
+
17
+ opts.on( '-a', '--all', 'FIX' ) do
18
+ options[:all] = true
19
+ end
20
+ end
21
+
22
+ optparse.parse!
23
+
24
+ if options[:all]
25
+ awake.all
26
+ else
27
+ awake.at(Date.today)
28
+ end
29
+ end
30
+
31
+ main
@@ -0,0 +1,17 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'achoo/ical'
4
+ require 'achoo/rc_loader'
5
+ require 'optparse'
6
+
7
+ include Achoo::RCLoader
8
+
9
+ def main
10
+ load_rc
11
+
12
+ RC[:ical].each do |config|
13
+ Achoo::ICal.from_http_request(config).print_events(Date.parse(ARGV[0]))
14
+ end
15
+ end
16
+
17
+ main
@@ -0,0 +1,31 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'achoo/rc_loader'
4
+ require 'achoo/vcs'
5
+ require 'date'
6
+ require 'optparse'
7
+
8
+ include Achoo::RCLoader
9
+
10
+ def main
11
+ load_rc
12
+
13
+ options = {
14
+ :date => Date.today,
15
+ }
16
+
17
+ optparse = OptionParser.new do|opts|
18
+ opts.banner = "Usage: $0 [options]"
19
+
20
+ opts.on( '-d', '--date [DATE]', 'FIX' ) do |date|
21
+ options[:date] = Date.parse(date)
22
+ end
23
+ end
24
+
25
+ optparse.parse!
26
+
27
+ Achoo::VCS.print_logs_for(options[:date], RC[:vcs_dirs])
28
+
29
+ end
30
+
31
+ main
@@ -0,0 +1,140 @@
1
+ class Achoo; end
2
+
3
+ require 'achoo/term'
4
+ require 'achoo/ui'
5
+ require 'logger'
6
+ require 'mechanize'
7
+
8
+
9
+
10
+ class Achoo
11
+
12
+ include Achoo::UI::Commands
13
+ include Achoo::UI::ExceptionHandling
14
+ include Achoo::UI::RegisterHours
15
+
16
+ def initialize(log=false)
17
+ @agent = Mechanize.new
18
+ if log
19
+ @agent.log = Logger.new("achoo_http.log")
20
+ end
21
+ end
22
+
23
+ def start
24
+ begin
25
+ print_welcome
26
+ login
27
+ scrape_urls
28
+ command_loop
29
+ rescue SystemExit => e
30
+ raise
31
+ rescue Exception => e
32
+ handle_fatal_exception("Something bad happened. Shutting down.", e)
33
+ end
34
+ end
35
+
36
+
37
+ def print_welcome
38
+ puts Term.shadowbox("Welcome to Achoo!")
39
+ end
40
+
41
+
42
+ def command_loop
43
+ while true
44
+ begin
45
+ trap("INT", "DEFAULT");
46
+ answer = Term.choose('[1]',
47
+ ["Register hours",
48
+ "Show flexitime balance",
49
+ "Day hour report",
50
+ "Week hour report",
51
+ "Holiday balance",
52
+ "Lock month",
53
+ ],
54
+ "Exit",
55
+ ['q', 'Q', ''])
56
+ dispatch(answer)
57
+ rescue Interrupt
58
+ puts # Add a new line in case we are prompting
59
+ end
60
+ end
61
+ end
62
+
63
+
64
+ def dispatch(command)
65
+ case command
66
+ when '0', 'q', 'Q'
67
+ exit
68
+ when '1', ''
69
+ register_hours(@agent)
70
+ when '2'
71
+ show_flexi_time(@agent)
72
+ when '3'
73
+ show_registered_hours_for_day(@agent)
74
+ when '4'
75
+ show_registered_hours_for_week(@agent)
76
+ when '5'
77
+ show_holiday_report(@agent)
78
+ when '6'
79
+ lock_month(@agent)
80
+ end
81
+ end
82
+
83
+
84
+ def scrape_urls
85
+ page = @agent.get(@agent.current_page.frames.find {|f| f.name == 'menu'}.href)
86
+ menu_links = page.search('a.menuItemLevel2')
87
+
88
+ RC[:hour_registration_url] = menu_link_to_url(menu_links, 'Time Registration')
89
+ RC[:lock_months_url] = menu_link_to_url(menu_links, 'Lock months')
90
+ RC[:holiday_report_url] = menu_link_to_url(menu_links, 'Holiday report')
91
+ RC[:hour_admin_url] = RC[:hour_registration_url]
92
+ end
93
+
94
+ def menu_link_to_url(menu_links, text)
95
+ a_tag = menu_links.find {|a|
96
+ a.text.strip == text
97
+ }
98
+
99
+ if a_tag.nil?
100
+ raise Exception.new("Could not find the '#{text}' link in Achievo.\nMake sure that language is 'English' and theme is 'no value' in Achievo's user preferences.\nYou must delete ~/.achoo_cookies.yml for these changes to take affect.")
101
+ end
102
+
103
+ url = a_tag.attribute('onclick').value.match(/window\.open\('([^']+)/)[1]
104
+ return "#{RC[:url]}#{url}"
105
+ end
106
+
107
+
108
+ def login
109
+ load_cookies
110
+
111
+ puts "Fetching data ..."
112
+ page = @agent.get(RC[:url])
113
+
114
+ return if page.forms.empty?
115
+
116
+ puts "Logging in ..."
117
+
118
+ form = page.forms.first
119
+ form.auth_user = RC[:user]
120
+ form.auth_pw = RC[:password]
121
+ page = @agent.submit(form, form.buttons.first)
122
+
123
+ if page.body.match(/Username and\/or password are incorrect. Please try again./)
124
+ raise "Username and/or password are incorrect."
125
+ end
126
+
127
+ @agent.cookie_jar.save_as("#{ENV['HOME']}/.achoo_cookies.yml")
128
+ end
129
+
130
+
131
+ def load_cookies
132
+ cookies_file = "#{ENV['HOME']}/.achoo_cookies.yml"
133
+ if FileTest.exists? cookies_file
134
+ @agent.cookie_jar.load(cookies_file)
135
+ end
136
+ end
137
+
138
+
139
+ end
140
+
@@ -0,0 +1,130 @@
1
+ require 'achoo/open_timespan'
2
+ require 'achoo/timespan'
3
+ require 'achoo/system'
4
+
5
+ class Achoo; end
6
+
7
+ class Achoo::Awake
8
+
9
+ def initialize
10
+ log = array_merge(wtmp, suspend) {|a, b| a[0] >= b[0] }
11
+ log.unshift([Time.now, :now])
12
+ @sessions = sessions(log)
13
+ end
14
+
15
+ def at(date)
16
+ span = Achoo::Timespan.new(date, date+1)
17
+ @sessions.each do |s|
18
+ print_session(s, span) if s[0].overlaps?(span)
19
+ end
20
+ end
21
+
22
+ def all
23
+ @sessions.each do |s|
24
+ print_session(s)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def print_session(s, span=nil)
31
+ puts "Powered on: " << s[0].to_s
32
+ s[1].each do |t|
33
+ puts " Awake: " << t.to_s if span.nil? || t.overlaps?(span)
34
+ end
35
+ end
36
+
37
+ def sessions(log)
38
+ sessions = []
39
+ group(log).each do |g|
40
+ raise "Session doesn't begin with a boot event" unless g.last[1] == :boot
41
+
42
+ session = []
43
+ case g.first[1]
44
+ when :now, :halt, :suspend
45
+ # We know the end of the session
46
+ session << Achoo::Timespan.new(g.last[0], g.first[0])
47
+ else # :awake, :boot
48
+ # We don't know the end of the session
49
+ session << Achoo::OpenTimespan.new(g.last[0], g.first[0])
50
+ g.unshift([-1, :crash])
51
+ end
52
+
53
+ raise "Session must consist of even number of events. Found #{g.length}" unless g.length.even?
54
+
55
+
56
+ session << []
57
+ unless g.length == 2
58
+ i = 0
59
+ while i < g.length-1
60
+ klass = g[i][1] == :crash ? Achoo::OpenTimespan : Achoo::Timespan
61
+ session[1] << klass.new(g[i+1][0], g[i][0])
62
+ i += 2
63
+ end
64
+ end
65
+
66
+ sessions << session
67
+ end
68
+ sessions
69
+ end
70
+
71
+ def array_merge(a1, a2)
72
+ array = []
73
+ until a1.empty? or a2.empty?
74
+ if yield(a1.first, a2.first)
75
+ array << a1.shift
76
+ else
77
+ array << a2.shift
78
+ end
79
+ end
80
+ array.concat(a1).concat(a2)
81
+ array
82
+ end
83
+
84
+ def suspend
85
+ log = Achoo::System::PMSuspend.new.reverse
86
+ new_log = []
87
+ log.each do |entry|
88
+ new_log << [entry.time, entry.action == 'Awake.' ? :awake : :suspend]
89
+ end
90
+ new_log
91
+ end
92
+
93
+ def wtmp
94
+ log = Achoo::System::Wtmp.new.reverse
95
+ new_log = []
96
+ log.each do |entry|
97
+ if entry.boot_event?
98
+ new_log << [entry.time, :boot]
99
+ elsif entry.halt_event?
100
+ new_log << [entry.time, :halt]
101
+ end
102
+ end
103
+ new_log
104
+ end
105
+
106
+ def group(log)
107
+ grouped_log = []
108
+ group = nil
109
+ log.each do |i|
110
+ if i[1] == :halt || i[1] == :now
111
+ group = [i]
112
+ else
113
+ if group.nil?
114
+ # Crashed
115
+ group = []
116
+ end
117
+ if i[1] == :boot
118
+ group << i
119
+ grouped_log << group
120
+ group = nil
121
+ else
122
+ group << i
123
+ end
124
+ end
125
+ end
126
+ grouped_log
127
+ end
128
+
129
+
130
+ end