caltime 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +15 -0
  2. data/bin/caltime +22 -0
  3. data/lib/caltime.rb +118 -0
  4. metadata +131 -0
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZmMxNDg4ODRiN2VmOWRjYjM5MzUyMDZiZDM2ZDhkZDI3NzI3MDJkYQ==
5
+ data.tar.gz: !binary |-
6
+ YjNhYTRmODhmZDZhZjViYTBkY2Y3ZDg1NWI3ZDE0MmQ0YzJiNmU3ZA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ M2VmNDE0MTQzNjU5ZTQzMTY3ZmRlODJiMTNjNGU1ZDE4ZTk3YjNkYjdmMjFk
10
+ OGZmZWIzMzZkMWFiYjBkNmIzMDIyM2JjNmY4OTVhODRjOGNjZTNlNzhlMGYz
11
+ MzhlZjEyMWI5NGI4M2VjMDUyZmZjMGZmYzdkYzhmZDdmMzdmYTk=
12
+ data.tar.gz: !binary |-
13
+ ZTFiY2VlYTZkYWNjNzZhMjZiZGZmOWZmODE3ZDE5MDBkMjJlYjAwZDY3NTZm
14
+ N2RmY2NmMzg5NzYwNTE3MmZjNmNhMWY4YTA5ODFhOTVmN2VjYjE5MGU2NTk5
15
+ MDc0NTlhZjU1M2EwZjBlYTlhZjg4YzBkNTRiZjhlOWE2YmU4NzI=
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ require 'caltime'
3
+ require 'highline/import'
4
+
5
+ c = Caltime.new
6
+ c.authenticate
7
+
8
+ timecard = c.timecard
9
+ punched_in = c.punched_in?
10
+
11
+ puts "Curent Timecard:"
12
+ puts timecard
13
+ puts ""
14
+ confirm = ask("You are currently punched #{punched_in ? "in" : "out"}. Would you like to punch #{punched_in ? "out" : "in"}? [y/n]: ") { |yn|
15
+ yn.validate = /[yn]/i
16
+ }
17
+ exit unless confirm.downcase == 'y'
18
+
19
+ c.punch
20
+ puts "Punch successfully recorded"
21
+
22
+
@@ -0,0 +1,118 @@
1
+ require 'mechanize'
2
+ require 'highline/import'
3
+ require 'nokogiri'
4
+ require 'terminal-table'
5
+ require 'colorize'
6
+
7
+ class Caltime
8
+
9
+ TIMESTAMP_URL = "https://caltimeprod.berkeley.edu/wfc/applications/wtk/html/ess/timestamp-record.jsp"
10
+ BASE_URL = "https://caltimeprod.berkeley.edu/"
11
+ SSO_URL = "https://caltimeprod.berkeley.edu/wfc/logonESS_SSO"
12
+
13
+ def initialize
14
+ @agent = Mechanize.new
15
+ @agent.follow_meta_refresh = true
16
+ @authed = false
17
+ end
18
+
19
+ def authenticate
20
+ page = @agent.get(BASE_URL)
21
+ loop do
22
+ count = 0
23
+ page = page.form_with(:id => "loginForm") do |form|
24
+ form.field_with(:id => 'username').value = ask("CalNet Username: ")
25
+ form.field_with(:id => 'password').value = ask("CalNet Password: ") { |q| q.echo = false }
26
+ end.submit
27
+ # Did we get redirected back to the login page?
28
+ if page.uri.to_s =~ /auth\.berkeley\.edu/
29
+ # Bail after 3 failures
30
+ count += 1
31
+ raise SecurityError("Too many failed authentication attempts") unless count < 3
32
+ puts "\nLogin incorrect, please try again."
33
+ else
34
+ break
35
+ end
36
+ end
37
+ # Strange extra SSO submission step
38
+ page.form_with(:action => SSO_URL).submit
39
+ # Let other methods know we are now authenticated
40
+ @authed = true
41
+ end
42
+
43
+ def punch
44
+ authenticate unless @authed
45
+ page = @agent.post(TIMESTAMP_URL)
46
+ if page.code != 200
47
+ # TODO: Raise some sort of custom error here
48
+ end
49
+ end
50
+
51
+ # Returns a Terminal::Table object that can be pretty-printed and contains timecard data
52
+ # TODO: Have this method return something less print-oriented and more data-oriented.
53
+ # Maybe something that can easily be converted to a terminal-table, idk
54
+ # For now, if called like CalTime.timecard raw: true, will return 2D array
55
+ def timecard(options={})
56
+ authenticate unless @authed
57
+ rows = fetch_timecard_rows
58
+ table_data = []
59
+ total = 0
60
+ rows.each do |row|
61
+ date, inpunch, outpunch, amount = timecard_row_to_array(row)
62
+ begin
63
+ total += amount.to_f
64
+ rescue
65
+ total += 0
66
+ end
67
+ table_data << [date, inpunch, outpunch, amount]
68
+ end
69
+ return table_data if options[:raw]
70
+ pretty_table = Terminal::Table.new :rows => table_data, :headings => ['Date', 'Punch In', 'Punch Out', 'Hours']
71
+ pretty_table.add_separator
72
+ pretty_table.add_row ["","","Total", total]
73
+ pretty_table.style = {:width => 50}
74
+ pretty_table
75
+ end
76
+
77
+ def punched_in?
78
+ today = Time.now.strftime("%a %-m/%-d")
79
+ latest_today_row = fetch_timecard_rows.select { |row| row.text =~ /#{today}/ }[-1]
80
+ punched_in = latest_today_row.css('td[class="InPunch"]').text =~ /[0-9]:[0-9][0-9]/
81
+ punched_out = latest_today_row.css('td[class="OutPunch"]').text =~ /[0-9]:[0-9][0-9]/
82
+ if (punched_in and punched_out) or (!punched_in and !punched_out)
83
+ return false
84
+ elsif punched_in
85
+ return true
86
+ else
87
+ # TODO: Raise some kind of custom error instead
88
+ return false
89
+ end
90
+ end
91
+
92
+ def punched_out?
93
+ return !punched_in?
94
+ end
95
+
96
+ private
97
+
98
+ def fetch_timecard_rows
99
+ page = @agent.get("https://caltimeprod.berkeley.edu/wfc/applications/mss/managerlaunch.do")
100
+ page = page.link_with(:text => "My Timecard").click
101
+ html = Nokogiri::HTML(page.body)
102
+ table = html.css('table[class="Tabular Timecard"]')
103
+ table.css('tbody tr')
104
+ end
105
+
106
+ def timecard_row_to_array(row)
107
+ red_line = " ".on_red
108
+ inpunch_el = row.css('td[class="InPunch"]')[0]
109
+ outpunch_el = row.css('td[class="OutPunch"]')[0]
110
+ date_el = row.css('td[class="Date"]')[0]
111
+ amount_el = row.css('td[class="ShiftTotal"]')[0]
112
+ inpunch = inpunch_el["title"] == "Missed In-Punch" ? red_line : inpunch_el.text.gsub(/[\n\t\r]/, "")
113
+ outpunch = outpunch_el["title"] == "Missed Out-Punch" ? red_line : outpunch_el.text.gsub(/[\n\t\r]/, "")
114
+ date = date_el.text
115
+ amount = amount_el.text.gsub(/[\n\t\r]/, "")
116
+ [date, inpunch, outpunch, amount]
117
+ end
118
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: caltime
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Nicholas Herson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mechanize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '2.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '2.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: highline
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: colorize
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '0.7'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: nokogiri
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: terminal-table
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '1.4'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '1.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: debugger
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '1.6'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '1.6'
97
+ description: Uses web scraping to punch in, punch out, report status, and display
98
+ a time card in a shell
99
+ email: nicholas.herson@gmail.com
100
+ executables:
101
+ - caltime
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - bin/caltime
106
+ - lib/caltime.rb
107
+ homepage:
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.4.5
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: CalTime CLI tool
131
+ test_files: []