achoo 0.3 → 0.4.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/CHANGES +12 -0
- data/README.rdoc +26 -30
- data/Rakefile +3 -0
- data/bin/achoo +2 -2
- data/lib/achoo.rb +1 -139
- data/lib/achoo/achievo.rb +13 -0
- data/lib/achoo/achievo/form.rb +22 -0
- data/lib/achoo/achievo/hour_administration_form.rb +91 -0
- data/lib/achoo/achievo/hour_registration_form.rb +230 -0
- data/lib/achoo/achievo/hour_registration_form_ranged.rb +49 -0
- data/lib/achoo/achievo/lock_month_form.rb +44 -0
- data/lib/achoo/achievo/login_form.rb +27 -0
- data/lib/achoo/achievo/table.rb +30 -0
- data/lib/achoo/app.rb +153 -0
- data/lib/achoo/awake.rb +86 -100
- data/lib/achoo/extensions.rb +24 -0
- data/lib/achoo/ical.rb +47 -40
- data/lib/achoo/rc_loader.rb +42 -42
- data/lib/achoo/system.rb +8 -3
- data/lib/achoo/system/cstruct.rb +67 -0
- data/lib/achoo/system/log_entry.rb +24 -0
- data/lib/achoo/system/pm_suspend.rb +17 -20
- data/lib/achoo/system/utmp_record.rb +64 -0
- data/lib/achoo/system/wtmp.rb +14 -10
- data/lib/achoo/temporal.rb +8 -0
- data/lib/achoo/temporal/open_timespan.rb +16 -0
- data/lib/achoo/temporal/timespan.rb +122 -0
- data/lib/achoo/term.rb +58 -56
- data/lib/achoo/term/menu.rb +2 -2
- data/lib/achoo/term/table.rb +59 -60
- data/lib/achoo/ui.rb +13 -9
- data/lib/achoo/ui/commands.rb +60 -38
- data/lib/achoo/ui/common.rb +10 -7
- data/lib/achoo/ui/date_chooser.rb +69 -65
- data/lib/achoo/ui/date_choosers.rb +15 -14
- data/lib/achoo/ui/exception_handling.rb +14 -12
- data/lib/achoo/ui/month_chooser.rb +37 -24
- data/lib/achoo/ui/optionally_ranged_date_chooser.rb +29 -25
- data/lib/achoo/ui/register_hours.rb +116 -114
- data/lib/achoo/vcs.rb +32 -30
- data/lib/achoo/vcs/git.rb +18 -14
- data/lib/achoo/vcs/subversion.rb +25 -23
- metadata +30 -24
- data/lib/achoo/binary.rb +0 -7
- data/lib/achoo/binary/cstruct.rb +0 -60
- data/lib/achoo/binary/utmp_record.rb +0 -59
- data/lib/achoo/form.rb +0 -18
- data/lib/achoo/hour_administration_form.rb +0 -131
- data/lib/achoo/hour_registration_form.rb +0 -227
- data/lib/achoo/hour_registration_form_ranged.rb +0 -45
- data/lib/achoo/lock_month_form.rb +0 -40
- data/lib/achoo/open_timespan.rb +0 -13
- data/lib/achoo/timespan.rb +0 -119
@@ -0,0 +1,24 @@
|
|
1
|
+
class Array
|
2
|
+
|
3
|
+
#
|
4
|
+
# Useful for merging two sorted arrays with Comparable
|
5
|
+
# elements.
|
6
|
+
#
|
7
|
+
# The content of the two input arrays should not be trusted after
|
8
|
+
# being mistreated by this method.
|
9
|
+
#
|
10
|
+
def merge!(other)
|
11
|
+
# FIX raise exception if merge is defined?
|
12
|
+
array = []
|
13
|
+
until empty? or other.empty?
|
14
|
+
if first <= other.first
|
15
|
+
array << shift
|
16
|
+
else
|
17
|
+
array << other.shift
|
18
|
+
end
|
19
|
+
end
|
20
|
+
array.concat(self).concat(other)
|
21
|
+
array
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/lib/achoo/ical.rb
CHANGED
@@ -1,58 +1,65 @@
|
|
1
|
-
require 'achoo
|
2
|
-
require 'achoo/
|
1
|
+
require 'achoo'
|
2
|
+
require 'achoo/temporal'
|
3
|
+
require 'achoo/ui'
|
3
4
|
require 'net/https'
|
4
5
|
require 'ri_cal'
|
5
6
|
|
6
|
-
|
7
|
+
module Achoo
|
8
|
+
class ICal
|
7
9
|
|
8
|
-
|
10
|
+
include UI::ExceptionHandling
|
9
11
|
|
10
|
-
|
12
|
+
@@cache = {}
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
def self.from_http_request(params)
|
15
|
+
return @@cache[params] if @@cache[params]
|
16
|
+
|
17
|
+
http = Net::HTTP.new(params[:host], params[:port])
|
18
|
+
http.use_ssl = true
|
19
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
20
|
+
ics = http.start do |http|
|
21
|
+
request = Net::HTTP::Get.new(params[:path])
|
22
|
+
request.basic_auth(params[:user], params[:pass])
|
23
|
+
response = http.request(request)
|
24
|
+
response.body
|
25
|
+
end
|
26
|
+
|
27
|
+
@@cache[params] = self.new(ics)
|
21
28
|
end
|
22
|
-
self.new(ics)
|
23
|
-
end
|
24
29
|
|
25
|
-
|
26
|
-
|
27
|
-
|
30
|
+
def initialize(ics_str)
|
31
|
+
@calendar = RiCal.parse_string(ics_str).first
|
32
|
+
end
|
28
33
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
34
|
+
def print_events(date, io=$stdout)
|
35
|
+
arg_start = date
|
36
|
+
arg_end = date + 1
|
37
|
+
|
38
|
+
@calendar.events.each do |e|
|
39
|
+
begin
|
40
|
+
if !e.x_properties['X-MICROSOFT-CDO-ALLDAYEVENT'].empty? && e.x_properties['X-MICROSOFT-CDO-ALLDAYEVENT'].first.value == 'TRUE'
|
41
|
+
# FIX handle this
|
42
|
+
elsif e.recurs?
|
43
|
+
e.occurrences({:overlapping => [arg_start, arg_end]}).each do |o|
|
44
|
+
print_event(o, io)
|
45
|
+
end
|
46
|
+
elsif e.dtstart >= arg_start && e.dtstart <= arg_end \
|
47
|
+
|| e.dtend >= arg_start && e.dtend <= arg_end
|
48
|
+
print_event(e, io)
|
40
49
|
end
|
41
|
-
|
42
|
-
|
43
|
-
print_event(e, io)
|
50
|
+
rescue Exception => e
|
51
|
+
handle_exception("Failed to process calendar event", e)
|
44
52
|
end
|
45
|
-
rescue Exception => e
|
46
|
-
handle_exception("Failed to process calendar event", e)
|
47
53
|
end
|
48
54
|
end
|
49
|
-
end
|
50
55
|
|
51
|
-
|
56
|
+
private
|
57
|
+
|
58
|
+
def print_event(e, io)
|
59
|
+
dti = Temporal::Timespan.new(e.dtstart.to_s, e.dtend.to_s)
|
60
|
+
io.printf "%s: %s\n", dti, e.summary
|
61
|
+
end
|
52
62
|
|
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
63
|
end
|
57
64
|
end
|
58
65
|
|
data/lib/achoo/rc_loader.rb
CHANGED
@@ -1,55 +1,55 @@
|
|
1
|
+
require 'achoo'
|
1
2
|
require 'achoo/term'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
module
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Achoo
|
6
|
+
module RCLoader
|
7
|
+
|
8
|
+
def load_rc(rc_file="#{ENV['HOME']}/.achoo")
|
9
|
+
#create_empty_rc_if_not_exists(rc_file)
|
10
|
+
file_permissions_secure?(rc_file)
|
11
|
+
|
12
|
+
self.class.const_set(:RC, YAML.load_file(rc_file))
|
13
|
+
if RC.is_a? String
|
14
|
+
abort "Failed to parse rc file. Do you use the old format? Please convert it to YAML."
|
15
|
+
end
|
16
|
+
|
17
|
+
verify_rc_contents(rc_file)
|
23
18
|
end
|
24
|
-
end
|
25
19
|
|
26
|
-
|
27
|
-
return if FileTest.exist?(rc_file)
|
20
|
+
private
|
28
21
|
|
29
|
-
|
30
|
-
|
31
|
-
|
22
|
+
def file_permissions_secure?(rc_file)
|
23
|
+
# FIX test to is to strict
|
24
|
+
if File.stat(rc_file).mode != 0100600
|
25
|
+
puts Term.fatal "Insecure permissions on #{rc_file}"
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
end
|
32
29
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
def create_empty_rc_if_not_exists(rc_file)
|
31
|
+
return if FileTest.exist?(rc_file)
|
32
|
+
|
33
|
+
FileUtils.touch(rc_file)
|
34
|
+
FileUtils.chmod(0600, rc_file)
|
37
35
|
end
|
38
36
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
def verify_rc_contents(rc_file)
|
38
|
+
%w(url user password).each do |key|
|
39
|
+
unless RC.has_key?(key.to_sym)
|
40
|
+
puts Term.fatal "Missing mandatory run control configuration variable: #{key}"
|
41
|
+
exit 1
|
42
|
+
end
|
43
43
|
end
|
44
|
-
end
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
45
|
+
%w(vcs_dirs ical).each do |key|
|
46
|
+
unless RC.has_key?(key.to_sym)
|
47
|
+
puts Term.warn "Missing run control configuration variable: #{key}. " \
|
48
|
+
+ "Add it to #{rc_file} to get rid of this warning"
|
49
|
+
RC[key] = []
|
50
|
+
end
|
51
51
|
end
|
52
52
|
end
|
53
|
-
end
|
54
53
|
|
54
|
+
end
|
55
55
|
end
|
data/lib/achoo/system.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
-
|
1
|
+
require 'achoo'
|
2
|
+
|
3
|
+
module Achoo
|
2
4
|
module System
|
3
|
-
autoload :
|
4
|
-
autoload :
|
5
|
+
autoload :CStruct, 'achoo/system/cstruct'
|
6
|
+
autoload :LogEntry, 'achoo/system/log_entry'
|
7
|
+
autoload :PMSuspend, 'achoo/system/pm_suspend'
|
8
|
+
autoload :UTMPRecord, 'achoo/system/utmp_record'
|
9
|
+
autoload :Wtmp, 'achoo/system/wtmp'
|
5
10
|
end
|
6
11
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'achoo/system'
|
2
|
+
|
3
|
+
module Achoo
|
4
|
+
module System
|
5
|
+
|
6
|
+
class CStruct
|
7
|
+
|
8
|
+
def initialize(bytes=nil)
|
9
|
+
@values = []
|
10
|
+
unpack(bytes) unless bytes.nil?
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
attr :template
|
16
|
+
|
17
|
+
def inherited(subclass)
|
18
|
+
subclass.instance_variable_set(:@template, '')
|
19
|
+
subclass.instance_variable_set(:@count, 0)
|
20
|
+
end
|
21
|
+
|
22
|
+
def char(name); add_type(name, :char, 'c', 0); end
|
23
|
+
def short(name); add_type(name, :short, 's', 0); end
|
24
|
+
def long(name); add_type(name, :long, 'l', 0); end
|
25
|
+
def quad(name); add_type(name, :quad, 'q', 0); end
|
26
|
+
|
27
|
+
def string(name, length)
|
28
|
+
add_type(name, :string, 'A', '', length)
|
29
|
+
end
|
30
|
+
|
31
|
+
def bin_size
|
32
|
+
@bin_size ||= template.split('').select {|c| c =~ /[[:alpha:]]/}.map do |c|
|
33
|
+
c == 'A' ? '' : 0
|
34
|
+
end.pack(template).length
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def add_type(name, type, temp, zero, length=nil)
|
40
|
+
template << temp
|
41
|
+
template << length.to_s if type == :string
|
42
|
+
index = @count
|
43
|
+
@count += 1
|
44
|
+
|
45
|
+
send(:define_method, name) do
|
46
|
+
@values[index]
|
47
|
+
end
|
48
|
+
|
49
|
+
send(:define_method, "#{name}=") do |val|
|
50
|
+
@values[index] = val
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
def unpack(str)
|
57
|
+
@values = str.unpack(self.class.template)
|
58
|
+
end
|
59
|
+
|
60
|
+
def pack
|
61
|
+
t = self.class.template.tr('A', 'a')
|
62
|
+
@values.pack(t)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'achoo/system'
|
2
|
+
|
3
|
+
module Achoo
|
4
|
+
module System
|
5
|
+
|
6
|
+
class LogEntry
|
7
|
+
include Comparable
|
8
|
+
|
9
|
+
attr :time
|
10
|
+
attr :event
|
11
|
+
|
12
|
+
def initialize(time, event)
|
13
|
+
@time = time
|
14
|
+
@event = event
|
15
|
+
end
|
16
|
+
|
17
|
+
def <=>(other_entry)
|
18
|
+
time <=> other_entry.time
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -1,30 +1,27 @@
|
|
1
1
|
require 'achoo/system'
|
2
2
|
require 'time'
|
3
3
|
|
4
|
-
|
4
|
+
module Achoo
|
5
|
+
module System
|
5
6
|
|
6
|
-
|
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
|
7
|
+
class PMSuspend < Array
|
15
8
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
9
|
+
def initialize(glob='/var/log/pm-suspend.log*')
|
10
|
+
super()
|
11
|
+
Dir.glob(glob).sort.reverse.each do |file|
|
12
|
+
next if file =~ /\.gz$/ # FIX uncompress?
|
13
|
+
File.open(file, 'r') do |fh|
|
14
|
+
fh.readlines.each do |l|
|
15
|
+
l.chop!
|
16
|
+
next unless l =~ /Awake|performing suspend/
|
17
|
+
time, event = *l.split(': ')
|
18
|
+
time = Time.parse(time)
|
19
|
+
self << LogEntry.new(time, event)
|
20
|
+
end
|
21
|
+
end
|
25
22
|
end
|
26
23
|
end
|
24
|
+
|
27
25
|
end
|
28
26
|
end
|
29
|
-
|
30
27
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'achoo/system'
|
2
|
+
|
3
|
+
module Achoo
|
4
|
+
module System
|
5
|
+
|
6
|
+
class UTMPRecord < CStruct
|
7
|
+
long :record_type
|
8
|
+
long :process_id
|
9
|
+
string :device_name, 32
|
10
|
+
string :inittab_id, 4
|
11
|
+
string :username, 32
|
12
|
+
string :hostname, 256
|
13
|
+
short :termination_status
|
14
|
+
short :exit_status
|
15
|
+
long :session_id
|
16
|
+
long :seconds
|
17
|
+
long :milliseconds
|
18
|
+
long :ip_address1
|
19
|
+
long :ip_address2
|
20
|
+
long :ip_address3
|
21
|
+
long :ip_address4
|
22
|
+
string :unused, 20
|
23
|
+
|
24
|
+
|
25
|
+
TYPE_MAP = [:empty,
|
26
|
+
:run_lvl,
|
27
|
+
:boot,
|
28
|
+
:new_time,
|
29
|
+
:old_time,
|
30
|
+
:init,
|
31
|
+
:login,
|
32
|
+
:normal,
|
33
|
+
:term,
|
34
|
+
:account,
|
35
|
+
]
|
36
|
+
|
37
|
+
def time
|
38
|
+
return nil if seconds.nil?
|
39
|
+
@time ||= Time.at(seconds, milliseconds)
|
40
|
+
end
|
41
|
+
|
42
|
+
def record_type_symbol
|
43
|
+
TYPE_MAP[record_type]
|
44
|
+
end
|
45
|
+
|
46
|
+
def record_type_symbol=(sym)
|
47
|
+
@values[0] = (TYPE_MAP.find_index(sym))
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
sprintf "%s %-7s %-8s %s", time.strftime('%F_%T'), record_type_symbol, username, device_name
|
52
|
+
end
|
53
|
+
|
54
|
+
def boot_event?
|
55
|
+
record_type_symbol == :boot
|
56
|
+
end
|
57
|
+
|
58
|
+
def halt_event?
|
59
|
+
record_type_symbol == :term && device_name == ':0'
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|