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,45 @@
1
+ require 'achoo/hour_registration_form'
2
+
3
+ class Achoo::HourRegistrationFormRanged < Achoo::HourRegistrationForm
4
+
5
+ def initialize(agent)
6
+ super
7
+
8
+ @page = @agent.get(atk_submit_to_url(@page.link_with(:text => 'Select range').href))
9
+ @form = @page.form('entryform')
10
+ end
11
+
12
+ def date=(date_range)
13
+ super(date_range[0])
14
+
15
+ to_day_field.value = date_range[1].strftime('%d')
16
+ to_month_field.value = date_range[1].strftime('%m')
17
+ to_year_field.value = date_range[1].year
18
+ end
19
+
20
+ def date
21
+ start = super
22
+ finish = Date.new(to_year_field.value.to_i, to_month_field.value.to_i,
23
+ to_day_field.value.to_i)
24
+ [start, finish]
25
+ end
26
+
27
+ private
28
+
29
+ def to_day_field
30
+ @form.field_with(:name => 'todate[day]')
31
+ end
32
+
33
+ def to_month_field
34
+ @form.field_with(:name => 'todate[month]')
35
+ end
36
+
37
+ def to_year_field
38
+ @form.field_with(:name => 'todate[year]')
39
+ end
40
+
41
+ def date_to_s
42
+ date.map {|d| d.strftime("%Y-%m-%d")}.join(" -> ")
43
+ end
44
+
45
+ end
@@ -0,0 +1,58 @@
1
+ require 'achoo/timespan'
2
+ require 'achoo/ui/exception_handling'
3
+ require 'net/https'
4
+ require 'ri_cal'
5
+
6
+ class Achoo; end
7
+
8
+ class Achoo::ICal
9
+
10
+ Achoo::UI::ExceptionHandling
11
+
12
+ def self.from_http_request(params)
13
+ http = Net::HTTP.new(params[:host], params[:port])
14
+ http.use_ssl = true
15
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
16
+ ics = http.start do |http|
17
+ request = Net::HTTP::Get.new(params[:path])
18
+ request.basic_auth(params[:user], params[:pass])
19
+ response = http.request(request)
20
+ response.body
21
+ end
22
+ self.new(ics)
23
+ end
24
+
25
+ def initialize(ics_str)
26
+ @calendar = RiCal.parse_string(ics_str).first
27
+ end
28
+
29
+ def print_events(date, io=$stdout)
30
+ arg_start = date
31
+ arg_end = date + 1
32
+
33
+ @calendar.events.each do |e|
34
+ begin
35
+ if !e.x_properties['X-MICROSOFT-CDO-ALLDAYEVENT'].empty? && e.x_properties['X-MICROSOFT-CDO-ALLDAYEVENT'].first.value == 'TRUE'
36
+ # FIX handle this
37
+ elsif e.recurs?
38
+ e.occurrences({:overlapping => [arg_start, arg_end]}).each do |o|
39
+ print_event(o, io)
40
+ end
41
+ elsif e.dtstart >= arg_start && e.dtstart <= arg_end \
42
+ || e.dtend >= arg_start && e.dtend <= arg_end
43
+ print_event(e, io)
44
+ end
45
+ rescue Exception => e
46
+ handle_exception("Failed to process calendar event", e)
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def print_event(e, io)
54
+ dti = Achoo::Timespan.new(e.dtstart.to_s, e.dtend.to_s)
55
+ io.printf "%s: %s\n", dti, e.summary
56
+ end
57
+ end
58
+
@@ -0,0 +1,40 @@
1
+ class Achoo; end
2
+
3
+ class Achoo::LockMonthForm
4
+
5
+ def initialize(agent)
6
+ @agent = agent
7
+ end
8
+
9
+ def lock_month(period)
10
+ page = @agent.get(RC[:lock_months_url])
11
+ @form = page.form('entryform')
12
+
13
+ @form.period = period
14
+ unless user_select.nil?
15
+ user_select.options.each do |opt|
16
+ if opt.text.match(/\(#{RC[:user]}\)$/)
17
+ opt.select
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ def print_values
25
+ puts "Month: #{@form.period}"
26
+ puts " User: #{user_select.value}" unless user_select.nil?
27
+
28
+ end
29
+
30
+ def submit
31
+ @form.submit
32
+ end
33
+
34
+ private
35
+
36
+ def user_select
37
+ @form.field_with(:name => 'userid')
38
+ end
39
+
40
+ end
@@ -0,0 +1,13 @@
1
+ require 'achoo/timespan'
2
+
3
+ class Achoo; end
4
+
5
+ class Achoo::OpenTimespan < Achoo::Timespan
6
+
7
+ def to_s
8
+ s = super
9
+ s.sub!(/^\([^)]+\)/, '(?+??:??)')
10
+ s.sub!(/- .*$/, '- ?')
11
+ s
12
+ end
13
+ end
@@ -0,0 +1,55 @@
1
+ require 'achoo/term'
2
+
3
+ class Achoo; end
4
+
5
+ module Achoo::RCLoader
6
+
7
+ def load_rc(rc_file="#{ENV['HOME']}/.achoo")
8
+ #create_empty_rc_if_not_exists(rc_file)
9
+ file_permissions_secure?(rc_file)
10
+
11
+ load rc_file
12
+
13
+ verify_rc_contents(rc_file)
14
+ end
15
+
16
+ private
17
+
18
+ def file_permissions_secure?(rc_file)
19
+ # FIX test to is to strict
20
+ if File.stat(rc_file).mode != 0100600
21
+ puts Achoo::Term.fatal "Insecure permissions on #{rc_file}"
22
+ exit 1
23
+ end
24
+ end
25
+
26
+ def create_empty_rc_if_not_exists(rc_file)
27
+ return if FileTest.exist?(rc_file)
28
+
29
+ FileUtils.touch(rc_file)
30
+ FileUtils.chmod(0600, rc_file)
31
+ end
32
+
33
+ def verify_rc_contents(rc_file)
34
+ unless Object.const_defined?('RC')
35
+ puts Achoo::Term.fatal "Malformed run control file: No RC constant defined"
36
+ exit 1
37
+ end
38
+
39
+ %w(url user password).each do |key|
40
+ unless RC.has_key?(key.to_sym)
41
+ puts Achoo::Term.fatal "Missing mandatory run control configuration variable: #{key}"
42
+ exit 1
43
+ end
44
+ end
45
+
46
+ %w(vcs_dirs ical).each do |key|
47
+ unless RC.has_key?(key.to_sym)
48
+ puts Achoo::Term.warn "Missing run control configuration variable: #{key}. " \
49
+ + "Add it to #{rc_file} to get rid of this warning"
50
+ RC[key] = []
51
+ end
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,6 @@
1
+ class Achoo
2
+ module System
3
+ autoload :Wtmp, 'achoo/system/wtmp.rb'
4
+ autoload :PMSuspend, 'achoo/system/pm_suspend.rb'
5
+ end
6
+ end
@@ -0,0 +1,30 @@
1
+ require 'achoo/system'
2
+ require 'time'
3
+
4
+ class Achoo::System::PMSuspend < Array
5
+
6
+ class LogEntry
7
+ attr :time
8
+ attr :action
9
+
10
+ def initialize(time_str, action)
11
+ @action = action
12
+ @time = Time.parse(time_str)
13
+ end
14
+ end
15
+
16
+ def initialize(glob='/var/log/pm-suspend.log*')
17
+ super()
18
+ Dir.glob(glob).sort.reverse.each do |file|
19
+ next if file =~ /\.gz$/ # FIX uncompress?
20
+ File.open(file, 'r') do |fh|
21
+ fh.readlines.each do |l|
22
+ l.chop!
23
+ next unless l =~ /Awake|performing suspend/
24
+ self << LogEntry.new(*l.split(': '))
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,18 @@
1
+ require 'achoo/binary'
2
+ require 'achoo/system'
3
+
4
+ class Achoo::System::Wtmp < Array
5
+
6
+ def initialize(glob='/var/log/wtmp*')
7
+ super()
8
+ chunk_size = Achoo::Binary::UTMPRecord.bin_size
9
+ Dir.glob(glob).sort.reverse.each do |file|
10
+ File.open(file, 'r') do |io|
11
+ while (bytes = io.read(chunk_size))
12
+ self << Achoo::Binary::UTMPRecord.new(bytes)
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+
3
+ class Achoo; class Term; end; end
4
+
5
+ Achoo::Term.autoload :Menu, 'achoo/term/menu'
6
+ Achoo::Term.autoload :Table, 'achoo/term/table'
7
+
8
+ class Achoo::Term
9
+
10
+ def self.bold(text); "\e[1m#{text}\e[0m"; end
11
+
12
+ def self.underline(text); "\e[4m#{text}\e[0m"; end
13
+
14
+ def self.warn(text); "\e[1;33m#{text}\e[0m"; end
15
+
16
+ def self.fatal(text); "\e[1;31m#{text}\e[0m"; end
17
+
18
+ def self.password
19
+ `stty -echo`
20
+ pas = ask('Password')
21
+ `stty echo`
22
+ pas
23
+ end
24
+
25
+ def self.ask(question='')
26
+ answer = nil
27
+ loop do
28
+ print bold("#{question}> ")
29
+ $stdout.flush
30
+ answer = gets
31
+
32
+ # Answer is nil if user hits C-d on an empty input
33
+ if answer.nil?
34
+ puts
35
+ exit
36
+ end
37
+
38
+ answer.strip! unless answer.nil?
39
+
40
+ # FIX move this to achoo.rb?
41
+ unless $stdin.tty?
42
+ puts answer
43
+ end
44
+ break unless a_little_something(answer)
45
+ end
46
+ answer
47
+ end
48
+
49
+ def self.choose(question, entries, special=nil, additional_valid_answers=[])
50
+ menu = Achoo::Term::Menu.new(question, entries, special, additional_valid_answers)
51
+ menu.print_ask_and_validate
52
+ end
53
+
54
+ private
55
+
56
+ def self.a_little_something(answer)
57
+ return false if answer.nil?
58
+
59
+ case answer.downcase
60
+ when 'bless you!', 'gesundheit!'
61
+ puts "Thank you!"
62
+ return true
63
+ else
64
+ return false
65
+ end
66
+ end
67
+
68
+ def self.shadowbox(text)
69
+ x = "┌──────────────────────────────────────────┐ \n"
70
+ x << "│ #{text.center(40)} " << "│▒\n"
71
+ x << "└──────────────────────────────────────────┘▒\n"
72
+ x << " ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒\n"
73
+ x
74
+ end
75
+
76
+ end
@@ -0,0 +1,55 @@
1
+ require 'achoo/term'
2
+
3
+ class Achoo
4
+ class Term
5
+ class Menu
6
+
7
+ def initialize(question, entries, special=nil, additional_valid_answers=[])
8
+ @question = question
9
+ @entries = entries
10
+ @special = special
11
+
12
+ @valid = {}
13
+ @valid['0'] = true unless @special.nil?
14
+ 1.upto(@entries.length).each {|i| @valid[i.to_s] = true}
15
+ additional_valid_answers.each {|a| @valid[a] = true}
16
+ end
17
+
18
+ def print_ask_and_validate()
19
+ return nil if @entries.empty?
20
+
21
+ print_menu()
22
+ return '1' if only_one_option?
23
+
24
+ loop do
25
+ answer = Achoo::Term.ask(@question)
26
+ if @valid[answer]
27
+ return answer
28
+ else
29
+ puts "Invalid value. Must be one of " << @valid.keys.sort.join(',')
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def print_menu
37
+ format = menu_item_format
38
+ @entries.each_with_index do |entry, i|
39
+ printf format, i+1, entry
40
+ end
41
+ printf format, 0, @special unless @special.nil?
42
+ end
43
+
44
+ def only_one_option?
45
+ @entries.length == 1 && @special.nil?
46
+ end
47
+
48
+ def menu_item_format
49
+ max_digits = Math.log10(@entries.length).floor + 1
50
+ " %#{max_digits}d. %s\n"
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,101 @@
1
+ # encoding: utf-8
2
+
3
+ if RUBY_VERSION < "1.9"
4
+ $KCODE = 'u'
5
+ require 'jcode'
6
+ end
7
+
8
+ # Unicode box drawing characters
9
+ #
10
+ # http://en.wikipedia.org/wiki/Box-drawing_characters
11
+ #
12
+ #
13
+ # 0 1 2 3 4 5 6 7 8 9 A B C D E F
14
+ #
15
+ # 2500 ─ ━ │ ┃ ┄ ┅ ┆ ┇ ┈ ┉ ┊ ┋ ┌ ┍ ┎ ┏
16
+ #
17
+ # 2510 ┐ ┑ ┒ ┓ └ ┕ ┖ ┗ ┘ ┙ ┚ ┛ ├ ┝ ┞ ┟
18
+ #
19
+ # 2520 ┠ ┡ ┢ ┣ ┤ ┥ ┦ ┧ ┨ ┩ ┪ ┫ ┬ ┭ ┮ ┯
20
+ #
21
+ # 2530 ┰ ┱ ┲ ┳ ┴ ┵ ┶ ┷ ┸ ┹ ┺ ┻ ┼ ┽ ┾ ┿
22
+ #
23
+ # 2540 ╀ ╁ ╂ ╃ ╄ ╅ ╆ ╇ ╈ ╉ ╊ ╋ ╌ ╍ ╎ ╏
24
+ #
25
+ # 2550 ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟
26
+ #
27
+ # 2560 ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ ╭ ╮ ╯
28
+ #
29
+ # 2570 ╰ ╱ ╲ ╳ ╴ ╵ ╶ ╷ ╸ ╹ ╺ ╻ ╼ ╽ ╾ ╿
30
+
31
+ class Achoo; class Term; end; end
32
+
33
+ class Achoo::Term::Table
34
+
35
+ def initialize(headers, data_rows, summaries=nil)
36
+ @headers = headers
37
+ @data_rows = data_rows
38
+ @summaries = summaries
39
+ end
40
+
41
+ def print(io=$stdout)
42
+ lengths = calculate_table_cell_widths
43
+ format = build_format(lengths)
44
+ headers = center_table_headers(lengths)
45
+ separator = lengths.map {|length| '─'*(length+2)}
46
+
47
+
48
+ io.print '┌' << separator.join('┬') << "┐\n"
49
+ io.print '│ ' << headers.join(' │ ') << " │\n"
50
+ io.print '├' << separator.join('┼') << "┤\n"
51
+ @data_rows.each {|r| io.printf format, *r }
52
+ unless @summaries.nil? || @data_rows.length == 1
53
+ io.print '├' << separator.join('┼') << "┤\n"
54
+ io.printf format, *@summaries
55
+ end
56
+ io.print '└' << separator.join('┴') << "┘\n"
57
+ end
58
+
59
+ private
60
+
61
+ def center_table_headers(lengths)
62
+ headers = @headers.dup
63
+ lengths.each_with_index do |len,i|
64
+ headers[i] = headers[i].center(len)
65
+ end
66
+ headers
67
+ end
68
+
69
+ def calculate_table_cell_widths
70
+ lengths = []
71
+ @headers.each_with_index do |h, i|
72
+ lengths[i] = RUBY_VERSION < '1.9' ? h.jlength : h.length
73
+ end
74
+ @data_rows.each do |r|
75
+ r.each_with_index do |d, i|
76
+ len = RUBY_VERSION < '1.9' ? d.jlength : d.length
77
+ lengths[i] = [len, lengths[i]].max
78
+ end
79
+ end
80
+ lengths
81
+ end
82
+
83
+ def build_format(lengths)
84
+ is_column_left_justified = Array.new(lengths.count)
85
+ is_column_left_justified.fill(false)
86
+
87
+ @data_rows.each do |r|
88
+ r.each_index do |c|
89
+ if !r[c].strip.empty? && !r[c].match(/^\d+[:.,]?\d*$/)
90
+ is_column_left_justified[c] = true
91
+ end
92
+ end
93
+ end
94
+
95
+ lengths.reduce('│') do |f, l|
96
+ justify = is_column_left_justified.shift ? '-' : ''
97
+ f + " %#{justify}#{l}s │"
98
+ end + "\n"
99
+ end
100
+
101
+ end