kobot 1.2.1 → 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dfc009d1da0376709ddb63ebec8176601a53cee00bc216c82554c403051d430f
4
- data.tar.gz: d7df58f052b9e2e945efd43c5cb11169c20d00b7d09a5d25ebfd3b688d1e16e8
3
+ metadata.gz: f35251b3ce8549b6b268a9741ecc88398f4fca4e1fbe27c343e2a1c35d456ca0
4
+ data.tar.gz: 1af276cc34607f075650b5be4df65c9666449c761b410fb5cc63629fe0392ff7
5
5
  SHA512:
6
- metadata.gz: 7728891bc95db5eb4dd2d9a4c4d20916930da12b32c82af6ee2458d4cb9e66a7c37cb4156a89f857b93141c22d06493b7f9bce4f5238786d0031bf8b0f64d0a6
7
- data.tar.gz: e4bc4ae0867ecfbe3bdd1018c9ffb9c578f34bf189f820e7344bbca7a7d5f209faee49e73d45f737161993cb2b3ea2f006695ee7f9870ea0dd1f7dea7417ad64
6
+ metadata.gz: a1a76b64eb1aa1019a12b4f1d4a2d34c84d79cf020b0ebc7cee7c1bd4902b180aec70790ee236883100bfd2501bbee9846f61b5b40dd8fb3996159e58b94b0df
7
+ data.tar.gz: a81c35396d3dde1a74ab9aad3c3535e92699268f49a133f9f43514b192bae808a84f1974c9f9c537690ce3561c942251c044591fc363b54c619b44586ede7670
data/CHANGELOG.md CHANGED
@@ -14,3 +14,17 @@
14
14
  - Improved logging for better readability in logs
15
15
  - Switched to builtin Logger#deprecate from Logger#warn for deprecations
16
16
  - Renamed internal method to skip? from holiday? as it was meant for skipping any specified date
17
+
18
+ ### v1.2.2
19
+ - Improved login screen wait and logging
20
+ - Applied fix for offenses about empty lines and long lines reported by Rubocop
21
+
22
+ ### v1.2.3
23
+ - Improved validation logic to skip running due to weekend or intentional skips
24
+ - Refactored engine by reducing methods length based on reports by Rubocop
25
+
26
+ ### v1.2.4
27
+ - Changed to use boolean values for validation instead of raising exceptions
28
+
29
+ ### v1.2.5
30
+ - Added I18n support for both English and Japanese KOT UI
data/bin/console CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'bundler/setup'
4
5
  require 'kobot'
data/lib/kobot/config.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobot
4
-
5
4
  # Configuration definition includes static ones hardcoded and
6
5
  # dynamic ones that can be specified by command line options.
7
6
  class Config
@@ -4,7 +4,6 @@ module Kobot
4
4
  # Credentials include id and password to login to KOT and
5
5
  # Gmail SMTP id and password to send email notifications.
6
6
  class Credential
7
-
8
7
  class << self
9
8
  attr_accessor :kot_id,
10
9
  :kot_password,
data/lib/kobot/engine.rb CHANGED
@@ -6,7 +6,6 @@ module Kobot
6
6
  # The core class that launches browser, logins to KOT, reads today
7
7
  # record, and conducts clock in or clock out action based on config.
8
8
  class Engine
9
-
10
9
  def initialize
11
10
  @now = Time.now.getlocal(Config.kot_timezone_offset)
12
11
  @today = @now.strftime(Config.kot_date_format)
@@ -15,7 +14,7 @@ module Kobot
15
14
 
16
15
  # The entrance where the whole flow starts.
17
16
  #
18
- # It exits early if today is weekend or treated as holiday by
17
+ # It exits early if today is weekend or marked as to skip by
19
18
  # the #{Config.skip} specified from command line option --skip.
20
19
  #
21
20
  # Unexpected behavior such as record appearing as holiday on
@@ -25,26 +24,12 @@ module Kobot
25
24
  # System errors or any unknown exceptions occurred if any are
26
25
  # to be popped up and should be handled by the outside caller.
27
26
  def start
28
- if weekend?
29
- if Config.force
30
- Kobot.logger.info("[Force] should have exited: today=#{@today} is weekend")
31
- else
32
- Kobot.logger.info("Today=#{@today} is weekend")
33
- return
34
- end
35
- end
36
- if skip?
37
- Kobot.logger.info("Today=#{@today} is skipped as per: --skip=#{Config.skip}")
38
- return
39
- end
40
- unless %i[in out].include? Config.clock
41
- Kobot.logger.warn("Invalid clock operation: #{Config.clock}")
42
- return
43
- end
27
+ return unless should_run_today?
28
+
44
29
  launch_browser
45
30
  login
46
31
  read_today_record
47
- verify_today_record!
32
+ validate_today_record!
48
33
  if Config.clock == :in
49
34
  clock_in!
50
35
  else
@@ -69,12 +54,23 @@ module Kobot
69
54
  Mailer.send(clock_notify_message(status: e.message))
70
55
  logout
71
56
  ensure
72
- Kobot.logger.info('Close browser')
73
- @browser&.quit
57
+ close_browser
74
58
  end
75
59
 
76
60
  private
77
61
 
62
+ def should_run_today?
63
+ if skip?
64
+ Kobot.logger.warn("Today=#{@today} is skipped as per: --skip=#{Config.skip}")
65
+ return false
66
+ end
67
+ return true unless weekend?
68
+
69
+ Kobot.logger.info("[Force] should have exited: today=#{@today} is weekend") if Config.force
70
+ Kobot.logger.warn("Today=#{@today} is weekend") unless Config.force
71
+ Config.force
72
+ end
73
+
78
74
  def launch_browser
79
75
  prefs = {
80
76
  profile: {
@@ -90,9 +86,24 @@ module Kobot
90
86
  Kobot.logger.info('Launch browser successful')
91
87
  end
92
88
 
89
+ def close_browser
90
+ return unless @browser
91
+
92
+ Kobot.logger.info('Close browser')
93
+ @browser.quit
94
+ end
95
+
93
96
  def login
94
97
  Kobot.logger.info("Navigate to: #{@top_url}")
95
98
  @browser.get @top_url
99
+ @wait.until { @browser.find_element(id: 'modal_window') }
100
+ modal_title_element = @wait.until { @browser.find_element(css: '.modal-title') }
101
+ @selector = if modal_title_element.text.downcase.include? 'password'
102
+ Selector.en
103
+ else
104
+ Selector.ja
105
+ end
106
+ Kobot.logger.info "Page title: #{@browser.title}"
96
107
  Kobot.logger.debug do
97
108
  "Login with id=#{Credential.kot_id} and password=#{Credential.kot_password}"
98
109
  end
@@ -101,10 +112,14 @@ module Kobot
101
112
  @browser.find_element(css: 'div.btn-control-message').click
102
113
 
103
114
  Kobot.logger.info 'Login successful'
104
- @wait.until { @browser.find_element(id: 'notification_content').text.include?('データを取得しました') }
115
+ @wait.until do
116
+ @browser.find_element(id: 'notification_content').text.include?(@selector.login_success_notification_text)
117
+ end
105
118
  if Config.browser_geolocation
106
119
  begin
107
- @wait.until { @browser.find_element(id: 'location_area').text.include?('位置情報取得済み') }
120
+ @wait.until do
121
+ @browser.find_element(id: 'location_area').text.include?(@selector.location_area_notification_text)
122
+ end
108
123
  rescue StandardError => e
109
124
  Kobot.logger.warn "Get geolocation failed: #{e.message}"
110
125
  end
@@ -114,21 +129,21 @@ module Kobot
114
129
 
115
130
  def logout
116
131
  if @browser.current_url.include? 'admin'
117
- Kobot.logger.info('Logout from タイムカード page')
132
+ Kobot.logger.info('Logout from Time Card (タイムカード) page')
118
133
  @browser.find_element(css: 'div.htBlock-header_logoutButton').click
119
134
  else
120
- Kobot.logger.info('Logout from Myレコーダー page')
135
+ Kobot.logger.info('Logout from My Recorder (Myレコーダー) page')
121
136
  @wait.until { @browser.find_element(id: 'menu_icon') }.click
122
- @wait.until { @browser.find_element(link: 'ログアウト') }.click
137
+ @wait.until { @browser.find_element(link: @selector.logout_menu_link_text) }.click
123
138
  @browser.switch_to.alert.accept
124
139
  end
125
140
  Kobot.logger.info 'Logout successful'
126
141
  end
127
142
 
128
143
  def read_today_record
129
- Kobot.logger.info('Navigate to タイムカード page')
144
+ Kobot.logger.info('Navigate to Time Card (タイムカード) page')
130
145
  @wait.until { @browser.find_element(id: 'menu_icon') }.click
131
- @wait.until { @browser.find_element(link: 'タイムカード') }.click
146
+ @wait.until { @browser.find_element(link: @selector.time_card_menu_link_text) }.click
132
147
 
133
148
  time_table = @wait.until { @browser.find_element(css: 'div.htBlock-adjastableTableF_inner > table') }
134
149
  time_table.find_elements(css: 'tbody > tr').each do |tr|
@@ -139,8 +154,12 @@ module Kobot
139
154
  @kot_today = date_cell.text
140
155
  @kot_today_css_class = date_cell.attribute('class')
141
156
  @kot_today_type = tr.find_element(css: 'td.work_day_type').text
142
- @kot_today_clock_in = tr.find_element(css: 'td.start_end_timerecord[data-ht-sort-index="START_TIMERECORD"]').text
143
- @kot_today_clock_out = tr.find_element(css: 'td.start_end_timerecord[data-ht-sort-index="END_TIMERECORD"]').text
157
+ @kot_today_clock_in = tr.find_element(
158
+ css: 'td.start_end_timerecord[data-ht-sort-index="START_TIMERECORD"]'
159
+ ).text
160
+ @kot_today_clock_out = tr.find_element(
161
+ css: 'td.start_end_timerecord[data-ht-sort-index="END_TIMERECORD"]'
162
+ ).text
144
163
  Kobot.logger.debug do
145
164
  {
146
165
  kot_toay: @kot_today,
@@ -154,30 +173,23 @@ module Kobot
154
173
  end
155
174
  end
156
175
 
157
- def verify_today_record!
176
+ def validate_today_record!
158
177
  raise KotRecordError, "Today=#{@today} is not found on kot" if @kot_today.strip.empty?
159
178
 
160
179
  if kot_weekend?
161
- unless Config.force
162
- raise KotRecordError,
163
- "Today=#{@today} is marked as weekend on kot: #{@kot_today}"
164
- end
180
+ raise KotRecordError, "Today=#{@today} is marked as weekend on kot: #{@kot_today}" unless Config.force
165
181
 
166
182
  Kobot.logger.info(
167
183
  "[Force] should have exited: today=#{@today} is marked as weekend on kot: #{@kot_today}"
168
184
  )
169
185
  end
170
186
 
171
- if kot_public_holiday?
172
- unless Config.force
173
- raise KotRecordError,
174
- "Today=#{@today} is marked as public holiday on kot: #{@kot_today}"
175
- end
187
+ return unless kot_public_holiday?
188
+ raise KotRecordError, "Today=#{@today} is marked as public holiday on kot: #{@kot_today}" unless Config.force
176
189
 
177
- Kobot.logger.info(
178
- "[Force] should have exited: today=#{@today} is marked as public holiday on kot: #{@kot_today}"
179
- )
180
- end
190
+ Kobot.logger.info(
191
+ "[Force] should have exited: today=#{@today} is marked as public holiday on kot: #{@kot_today}"
192
+ )
181
193
  end
182
194
 
183
195
  def clock_in!
@@ -224,9 +236,9 @@ module Kobot
224
236
  @browser.get @top_url
225
237
  clock_in_button = @wait.until { @browser.find_element(css: 'div.record-clock-in') }
226
238
  if Config.dryrun
227
- Kobot.logger.info('[Dryrun] clock in button (出勤) would have been clicked')
239
+ Kobot.logger.info('[Dryrun] Clock-in button (出勤) would have been clicked')
228
240
  else
229
- Kobot.logger.info('Clicking the clock in button (出勤)')
241
+ Kobot.logger.info('Clicking the Clock-in button (出勤)')
230
242
  clock_in_button.click
231
243
  end
232
244
  end
@@ -236,9 +248,9 @@ module Kobot
236
248
  @browser.get @top_url
237
249
  clock_out_button = @wait.until { @browser.find_element(css: 'div.record-clock-out') }
238
250
  if Config.dryrun
239
- Kobot.logger.info('[Dryrun] clock out button (退勤) would have been clicked')
251
+ Kobot.logger.info('[Dryrun] Clock-out button (退勤) would have been clicked')
240
252
  else
241
- Kobot.logger.info('Clicking the clock in button (退勤)')
253
+ Kobot.logger.info('Clicking the Clock-out button (退勤)')
242
254
  clock_out_button.click
243
255
  end
244
256
  end
@@ -255,18 +267,21 @@ module Kobot
255
267
  end
256
268
 
257
269
  def kot_weekend?
258
- %w[土 日].any? { |kanji| @kot_today&.include? kanji }
270
+ [
271
+ @selector.kot_date_saturday,
272
+ @selector.kot_date_sunday
273
+ ].any? { |weekend| @kot_today&.include? weekend }
259
274
  end
260
275
 
261
276
  def kot_public_holiday?
262
- return true if @kot_today_type&.include? '休日'
277
+ return true if @kot_today_type&.include? @selector.kot_workday_time_off_text
263
278
 
264
279
  kot_today_highlighted = %w[sunday saturday].any? do |css|
265
280
  @kot_today_css_class&.include? css
266
281
  end
267
282
  if kot_today_highlighted
268
283
  Kobot.logger.warn(
269
- "Today=#{@kot_today} is highlighted (holiday) but not marked as 休日"
284
+ "Today=#{@kot_today} is highlighted (holiday) but not marked as #{@selector.kot_workday_time_off_text}"
270
285
  )
271
286
  end
272
287
  kot_today_highlighted
@@ -1,14 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobot
4
-
5
- class KotRecordError < StandardError
6
- end
7
-
8
- class KotClockInError < StandardError
9
- end
10
-
11
- class KotClockOutError < StandardError
12
- end
13
-
4
+ class KotRecordError < StandardError; end
5
+ class KotClockInError < StandardError; end
6
+ class KotClockOutError < StandardError; end
14
7
  end
data/lib/kobot/mailer.rb CHANGED
@@ -3,11 +3,9 @@
3
3
  require 'net/smtp'
4
4
 
5
5
  module Kobot
6
-
7
6
  # Responsible for sending email notifications in SMTP with Gmail
8
7
  class Mailer
9
8
  class << self
10
-
11
9
  # Sends email in preconfigured Gmail SMTP credential and to the recipient
12
10
  # configured by #{Config.gmail_notify_to} or self if not configured, with
13
11
  # email subject set by #{Config.gmail_notify_subject}.
data/lib/kobot/option.rb CHANGED
@@ -3,11 +3,9 @@
3
3
  require 'optparse'
4
4
 
5
5
  module Kobot
6
-
7
6
  # Responsible for parsing the command line options for custom execution.
8
7
  class Option
9
8
  class << self
10
-
11
9
  # Parses command line options and returns a hash containing the options.
12
10
  def parse!
13
11
  options = {}
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kobot
4
+ # The texts used in selectors to identify the elements on KOT UI.
5
+ class Selector
6
+ attr_accessor :login_success_notification_text,
7
+ :location_area_notification_text,
8
+ :time_card_menu_link_text,
9
+ :kot_date_saturday,
10
+ :kot_date_sunday,
11
+ :kot_workday_time_off_text,
12
+ :logout_menu_link_text
13
+
14
+ class << self
15
+ def en
16
+ selector = Selector.new
17
+ selector.login_success_notification_text = 'Data has been obtained'
18
+ selector.location_area_notification_text = 'Obtained location'
19
+ selector.time_card_menu_link_text = 'Time Card'
20
+ selector.kot_date_saturday = 'Sat'
21
+ selector.kot_date_sunday = 'Sun'
22
+ selector.kot_workday_time_off_text = 'time-off'
23
+ selector.logout_menu_link_text = 'Sign out'
24
+ selector
25
+ end
26
+
27
+ def ja
28
+ selector = Selector.new
29
+ selector.login_success_notification_text = 'データを取得しました'
30
+ selector.location_area_notification_text = '位置情報取得済み'
31
+ selector.time_card_menu_link_text = 'タイムカード'
32
+ selector.kot_date_saturday = '土'
33
+ selector.kot_date_sunday = '日'
34
+ selector.kot_workday_time_off_text = '休日'
35
+ selector.logout_menu_link_text = 'ログアウト'
36
+ selector
37
+ end
38
+ end
39
+ end
40
+ end
data/lib/kobot/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobot
4
- VERSION = '1.2.1'
4
+ VERSION = '1.2.5'
5
5
  end
data/lib/kobot.rb CHANGED
@@ -4,6 +4,7 @@ require 'kobot/version'
4
4
  require 'kobot/exception'
5
5
  require 'kobot/option'
6
6
  require 'kobot/config'
7
+ require 'kobot/selector'
7
8
  require 'kobot/credential'
8
9
  require 'kobot/logger'
9
10
  require 'kobot/mailer'
@@ -14,8 +15,6 @@ require 'kobot/engine'
14
15
  # and with Google Gmail service email notification can also be sent to notify the results.
15
16
  module Kobot
16
17
  class << self
17
-
18
- # The entrance to run Kobot.
19
18
  def run
20
19
  configure
21
20
  Engine.new.start
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kobot
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Jiang
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-27 00:00:00.000000000 Z
11
+ date: 2021-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: webdrivers
@@ -83,6 +83,7 @@ files:
83
83
  - lib/kobot/logger.rb
84
84
  - lib/kobot/mailer.rb
85
85
  - lib/kobot/option.rb
86
+ - lib/kobot/selector.rb
86
87
  - lib/kobot/version.rb
87
88
  homepage: https://github.com/yuan-jiang/kobot
88
89
  licenses:
@@ -107,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
108
  - !ruby/object:Gem::Version
108
109
  version: '0'
109
110
  requirements: []
110
- rubygems_version: 3.0.3
111
+ rubygems_version: 3.1.2
111
112
  signing_key:
112
113
  specification_version: 4
113
114
  summary: Kobot automates the clock in/out of KING OF TIME.