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.
- checksums.yaml +15 -0
- data/bin/caltime +22 -0
- data/lib/caltime.rb +118 -0
- metadata +131 -0
checksums.yaml
ADDED
@@ -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=
|
data/bin/caltime
ADDED
@@ -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
|
+
|
data/lib/caltime.rb
ADDED
@@ -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: []
|