caltime 0.0.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.
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: []