timet 1.6.1.1 → 1.6.3
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 +4 -4
- data/.qlty/qlty.toml +0 -0
- data/.reek.yml +17 -1
- data/.rubocop.yml +6 -1
- data/CHANGELOG.md +37 -4
- data/README.md +40 -24
- data/lib/timet/application.rb +3 -3
- data/lib/timet/database.rb +32 -25
- data/lib/timet/database_syncer.rb +93 -131
- data/lib/timet/discord_notifier.rb +3 -1
- data/lib/timet/s3_supabase.rb +74 -131
- data/lib/timet/table.rb +0 -4
- data/lib/timet/tag_distribution.rb +181 -160
- data/lib/timet/time_helper.rb +18 -3
- data/lib/timet/time_report.rb +17 -3
- data/lib/timet/utils.rb +8 -8
- data/lib/timet/validation_editor.rb +303 -0
- data/lib/timet/version.rb +2 -2
- metadata +6 -9
- data/lib/timet/item_data_helper.rb +0 -59
- data/lib/timet/time_report_helper.rb +0 -36
- data/lib/timet/time_update_helper.rb +0 -75
- data/lib/timet/time_validation_helper.rb +0 -175
- data/lib/timet/validation_edit_helper.rb +0 -160
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'time'
|
|
4
|
-
|
|
5
|
-
module Timet
|
|
6
|
-
# Helper module for time validation logic.
|
|
7
|
-
module TimeValidationHelper
|
|
8
|
-
# Parses the time string and raises an ArgumentError if the format is invalid.
|
|
9
|
-
#
|
|
10
|
-
# @param time_str [String] The time string to parse.
|
|
11
|
-
#
|
|
12
|
-
# @return [Time] The parsed time component.
|
|
13
|
-
#
|
|
14
|
-
# @raise [ArgumentError] If the time string is not in a valid format.
|
|
15
|
-
def parse_time_string(time_str)
|
|
16
|
-
Time.parse(time_str)
|
|
17
|
-
rescue ArgumentError
|
|
18
|
-
raise ArgumentError, "Invalid time format: #{time_str}"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Adjusts the end datetime if it's earlier than or same as the start time, assuming it's for the next day.
|
|
22
|
-
#
|
|
23
|
-
# @param field [String] The field being validated ('start' or 'end').
|
|
24
|
-
# @param start_timestamp [Integer, nil] The start timestamp of the item.
|
|
25
|
-
# @param new_datetime [Time] The new datetime object.
|
|
26
|
-
#
|
|
27
|
-
# @return [Time] The adjusted datetime object.
|
|
28
|
-
def adjust_end_datetime(field, start_timestamp, new_datetime)
|
|
29
|
-
# If setting 'end' time and the parsed new_datetime (based on start_date)
|
|
30
|
-
# is earlier than or same as start_time, assume it's for the next calendar day.
|
|
31
|
-
if field == 'end' && start_timestamp && (new_datetime.to_i <= start_timestamp)
|
|
32
|
-
new_datetime += (24 * 60 * 60) # Add one day
|
|
33
|
-
end
|
|
34
|
-
new_datetime
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Determines the base date and time for creating a new datetime object.
|
|
38
|
-
#
|
|
39
|
-
# @param item [Array] The item being modified.
|
|
40
|
-
# @param field [String] The field being validated ('start' or 'end').
|
|
41
|
-
# @param start_timestamp [Integer, nil] The start timestamp of the item.
|
|
42
|
-
#
|
|
43
|
-
# @return [Time] The base date and time.
|
|
44
|
-
#
|
|
45
|
-
# @raise [ArgumentError] If the field is 'end' and the start timestamp is not set or if the field is invalid.
|
|
46
|
-
def determine_base_date_time(_item, field, start_timestamp)
|
|
47
|
-
case field
|
|
48
|
-
when 'start'
|
|
49
|
-
determine_start_base_date_time(start_timestamp)
|
|
50
|
-
when 'end'
|
|
51
|
-
determine_end_base_date_time(start_timestamp)
|
|
52
|
-
else
|
|
53
|
-
raise ArgumentError, "Invalid field: #{field}"
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Determines the base date and time for the 'start' field.
|
|
58
|
-
#
|
|
59
|
-
# @param start_timestamp [Integer, nil] The start timestamp of the item.
|
|
60
|
-
#
|
|
61
|
-
# @return [Time] The base date and time.
|
|
62
|
-
def determine_start_base_date_time(start_timestamp)
|
|
63
|
-
start_timestamp ? Time.at(start_timestamp) : Time.now
|
|
64
|
-
end
|
|
65
|
-
private :determine_start_base_date_time
|
|
66
|
-
|
|
67
|
-
# Determines the base date and time for the 'end' field.
|
|
68
|
-
#
|
|
69
|
-
# @param start_timestamp [Integer, nil] The start timestamp of the item.
|
|
70
|
-
#
|
|
71
|
-
# @return [Time] The base date and time.
|
|
72
|
-
#
|
|
73
|
-
# @raise [ArgumentError] If the start timestamp is not set.
|
|
74
|
-
def determine_end_base_date_time(start_timestamp)
|
|
75
|
-
# This ensures that start_timestamp is not nil when setting/editing an end time.
|
|
76
|
-
raise ArgumentError, "Cannot set 'end' time because 'start' time is not set." unless start_timestamp
|
|
77
|
-
|
|
78
|
-
Time.at(start_timestamp)
|
|
79
|
-
end
|
|
80
|
-
private :determine_end_base_date_time
|
|
81
|
-
|
|
82
|
-
# Creates a new datetime object based on the parsed time component.
|
|
83
|
-
#
|
|
84
|
-
# @param _base_date_time [Time] The base date and time (not used for date components).
|
|
85
|
-
# @param parsed_time_component [Time] The parsed time component.
|
|
86
|
-
#
|
|
87
|
-
# @return [Time] The new datetime object.
|
|
88
|
-
def create_new_datetime(_base_date_time, parsed_time_component)
|
|
89
|
-
Time.new(
|
|
90
|
-
parsed_time_component.year,
|
|
91
|
-
parsed_time_component.month,
|
|
92
|
-
parsed_time_component.day,
|
|
93
|
-
parsed_time_component.hour,
|
|
94
|
-
parsed_time_component.min,
|
|
95
|
-
parsed_time_component.sec,
|
|
96
|
-
parsed_time_component.utc_offset # Preserve timezone context
|
|
97
|
-
)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# Validates that the new datetime is not in the future.
|
|
101
|
-
#
|
|
102
|
-
# @param new_datetime [Time] The new datetime object.
|
|
103
|
-
#
|
|
104
|
-
# @raise [ArgumentError] If the new datetime is in the future.
|
|
105
|
-
def validate_future_date(new_datetime)
|
|
106
|
-
# Ensure the new datetime is not in the future relative to the current time.
|
|
107
|
-
return unless new_datetime > Time.now.getlocal
|
|
108
|
-
|
|
109
|
-
raise ArgumentError, "Cannot set time to a future date or time: #{new_datetime.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Validates that the difference between two timestamps is less than 24 hours.
|
|
113
|
-
#
|
|
114
|
-
# @param timestamp1 [Integer] The first timestamp.
|
|
115
|
-
# @param timestamp2 [Integer] The second timestamp.
|
|
116
|
-
#
|
|
117
|
-
# @raise [ArgumentError] If the difference is >= 24 hours.
|
|
118
|
-
def validate_time_difference(timestamp1, timestamp2)
|
|
119
|
-
return unless (timestamp2 - timestamp1).abs >= 24 * 60 * 60
|
|
120
|
-
|
|
121
|
-
raise ArgumentError, 'The difference between start and end time must be less than 24 hours.'
|
|
122
|
-
end
|
|
123
|
-
private :validate_time_difference
|
|
124
|
-
|
|
125
|
-
# Validates the time order (start before end, end after start).
|
|
126
|
-
#
|
|
127
|
-
# @param new_epoch [Integer] The new time in epoch format.
|
|
128
|
-
# @param reference_timestamp [Integer] The reference timestamp (start or end).
|
|
129
|
-
# @param new_datetime [Time] The new datetime object.
|
|
130
|
-
# @param field [String] The field being validated ('start' or 'end').
|
|
131
|
-
#
|
|
132
|
-
# @raise [ArgumentError] If the time order is invalid.
|
|
133
|
-
def validate_time_order(new_epoch, reference_timestamp, new_datetime, field)
|
|
134
|
-
case field
|
|
135
|
-
when 'end'
|
|
136
|
-
if new_epoch <= reference_timestamp
|
|
137
|
-
raise ArgumentError,
|
|
138
|
-
"End time (#{new_datetime.strftime('%Y-%m-%d %H:%M:%S')}) must be after start time " \
|
|
139
|
-
"(#{Time.at(reference_timestamp).strftime('%Y-%m-%d %H:%M:%S')})."
|
|
140
|
-
end
|
|
141
|
-
when 'start'
|
|
142
|
-
if new_epoch >= reference_timestamp
|
|
143
|
-
raise ArgumentError,
|
|
144
|
-
"Start time (#{new_datetime.strftime('%Y-%m-%d %H:%M:%S')}) must be before end time " \
|
|
145
|
-
"(#{Time.at(reference_timestamp).strftime('%Y-%m-%d %H:%M:%S')})."
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
private :validate_time_order
|
|
150
|
-
|
|
151
|
-
# Validates the end time against the start time.
|
|
152
|
-
#
|
|
153
|
-
# @param new_epoch [Integer] The new end time in epoch format.
|
|
154
|
-
# @param start_timestamp [Integer] The start timestamp of the item.
|
|
155
|
-
# @param new_datetime [Time] The new datetime object.
|
|
156
|
-
#
|
|
157
|
-
# @raise [ArgumentError] If the end time is not after the start time or the difference is >= 24 hours.
|
|
158
|
-
def validate_end_time(new_epoch, start_timestamp, new_datetime)
|
|
159
|
-
validate_time_order(new_epoch, start_timestamp, new_datetime, 'end')
|
|
160
|
-
validate_time_difference(start_timestamp, new_epoch)
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
# Validates the start time against the end time.
|
|
164
|
-
#
|
|
165
|
-
# @param new_epoch [Integer] The new start time in epoch format.
|
|
166
|
-
# @param end_timestamp [Integer] The end timestamp of the item.
|
|
167
|
-
# @param new_datetime [Time] The new datetime object.
|
|
168
|
-
#
|
|
169
|
-
# @raise [ArgumentError] If the start time is not before the end time or the difference is >= 24 hours.
|
|
170
|
-
def validate_start_time(new_epoch, end_timestamp, new_datetime)
|
|
171
|
-
validate_time_order(new_epoch, end_timestamp, new_datetime, 'start')
|
|
172
|
-
validate_time_difference(new_epoch, end_timestamp)
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
end
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'time_helper'
|
|
4
|
-
require_relative 'item_data_helper'
|
|
5
|
-
require_relative 'time_update_helper'
|
|
6
|
-
require_relative 'time_validation_helper'
|
|
7
|
-
|
|
8
|
-
module Timet
|
|
9
|
-
# Validates and updates a specific field of an item based on certain conditions.
|
|
10
|
-
# If the field is 'start' or 'end', it checks and updates the value accordingly.
|
|
11
|
-
# Otherwise, it directly updates the field with the new value.
|
|
12
|
-
module ValidationEditHelper
|
|
13
|
-
include TimeValidationHelper
|
|
14
|
-
|
|
15
|
-
# Constants for time fields.
|
|
16
|
-
TIME_FIELDS = %w[start end].freeze
|
|
17
|
-
|
|
18
|
-
# Validates and updates an item's attribute based on the provided field and new value.
|
|
19
|
-
#
|
|
20
|
-
# @param item [Array] The item to be updated.
|
|
21
|
-
# @param field [String] The field to be updated.
|
|
22
|
-
# @param new_value [String] The new value for the field.
|
|
23
|
-
#
|
|
24
|
-
# @return [Array] The updated item.
|
|
25
|
-
#
|
|
26
|
-
# @raise [ArgumentError] If the field is invalid or the new value is invalid.
|
|
27
|
-
def validate_and_update(item, field, new_value)
|
|
28
|
-
case field
|
|
29
|
-
when 'notes'
|
|
30
|
-
item[4] = new_value
|
|
31
|
-
when 'tag'
|
|
32
|
-
item[3] = new_value
|
|
33
|
-
when 'start'
|
|
34
|
-
item[1] = validate_time(item, 'start', new_value)
|
|
35
|
-
when 'end'
|
|
36
|
-
item[2] = validate_time(item, 'end', new_value)
|
|
37
|
-
else
|
|
38
|
-
raise ArgumentError, "Invalid field: #{field}"
|
|
39
|
-
end
|
|
40
|
-
item
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Validates if a given time string is in a valid format.
|
|
44
|
-
#
|
|
45
|
-
# @param item [Array] The item being modified.
|
|
46
|
-
# @param field [String] The field being validated ('start' or 'end').
|
|
47
|
-
# @param time_str [String, nil] The new time string (e.g., "HH:MM" or "HH:MM:SS").
|
|
48
|
-
# If nil or empty, it signifies that the original time should be kept.
|
|
49
|
-
#
|
|
50
|
-
# @return [Integer, nil] The validated time as an integer epoch.
|
|
51
|
-
# Returns the original timestamp for the field if time_str is nil/empty.
|
|
52
|
-
# Returns nil if the original field was nil and time_str is nil/empty.
|
|
53
|
-
#
|
|
54
|
-
# @raise [ArgumentError] If the time string is not in a valid format.
|
|
55
|
-
def validate_time(item, field, time_str)
|
|
56
|
-
# If time_str is nil or empty, user pressed Enter, meaning no change to this field.
|
|
57
|
-
return field == 'start' ? item[1] : item[2] if time_str.nil? || time_str.strip.empty?
|
|
58
|
-
|
|
59
|
-
parsed_time_component = parse_time_string(time_str)
|
|
60
|
-
|
|
61
|
-
start_timestamp = item[1]
|
|
62
|
-
end_timestamp = item[2]
|
|
63
|
-
|
|
64
|
-
new_datetime = determine_and_create_datetime(item, field, start_timestamp, parsed_time_component)
|
|
65
|
-
|
|
66
|
-
new_epoch = new_datetime.to_i
|
|
67
|
-
|
|
68
|
-
perform_validation(
|
|
69
|
-
item: item,
|
|
70
|
-
field: field,
|
|
71
|
-
new_epoch: new_epoch,
|
|
72
|
-
start_timestamp: start_timestamp,
|
|
73
|
-
end_timestamp: end_timestamp,
|
|
74
|
-
new_datetime: new_datetime
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
new_epoch
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Validates that the new start or end time does not collide with existing entries.
|
|
81
|
-
#
|
|
82
|
-
# @param item [Array] The item being modified.
|
|
83
|
-
# @param field [String] The field being validated ('start' or 'end').
|
|
84
|
-
# @param new_epoch [Integer] The new time in epoch format.
|
|
85
|
-
#
|
|
86
|
-
# @raise [ArgumentError] If the new time collides with a previous or next item.
|
|
87
|
-
def validate_time_collisions(item, field, new_epoch)
|
|
88
|
-
item_id = item[0]
|
|
89
|
-
prev_item = @db.find_item(item_id - 1)
|
|
90
|
-
next_item = @db.find_item(item_id + 1)
|
|
91
|
-
|
|
92
|
-
check_collision_with_previous_item(field, new_epoch, prev_item)
|
|
93
|
-
check_collision_with_next_item(field, new_epoch, next_item)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Checks for collision with the previous item.
|
|
97
|
-
def check_collision_with_previous_item(field, new_epoch, prev_item)
|
|
98
|
-
return unless prev_item && field == 'start' && new_epoch < prev_item[2]
|
|
99
|
-
|
|
100
|
-
raise ArgumentError,
|
|
101
|
-
'New start time collides with previous item (ends at ' \
|
|
102
|
-
"#{Time.at(prev_item[2]).strftime('%Y-%m-%d %H:%M:%S')})."
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# Checks for collision with the next item.
|
|
106
|
-
def check_collision_with_next_item(field, new_epoch, next_item)
|
|
107
|
-
return unless next_item
|
|
108
|
-
|
|
109
|
-
if field == 'start' && new_epoch >= next_item[1]
|
|
110
|
-
raise ArgumentError,
|
|
111
|
-
'New start time collides with next item (starts at ' \
|
|
112
|
-
"#{Time.at(next_item[1]).strftime('%Y-%m-%d %H:%M:%S')})."
|
|
113
|
-
elsif field == 'end' && new_epoch > next_item[1]
|
|
114
|
-
raise ArgumentError,
|
|
115
|
-
'New end time collides with next item (starts at ' \
|
|
116
|
-
"#{Time.at(next_item[1]).strftime('%Y-%m-%d %H:%M:%S')})."
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
private
|
|
121
|
-
|
|
122
|
-
# Determines the base date and time, creates a new datetime object, and adjusts it if necessary.
|
|
123
|
-
#
|
|
124
|
-
# @param item [Array] The item being modified.
|
|
125
|
-
# @param field [String] The field being validated ('start' or 'end').
|
|
126
|
-
# @param start_timestamp [Integer, nil] The start timestamp of the item.
|
|
127
|
-
# @param parsed_time_component [Time] The parsed time component.
|
|
128
|
-
#
|
|
129
|
-
# @return [Time] The new datetime object.
|
|
130
|
-
def determine_and_create_datetime(item, field, start_timestamp, parsed_time_component)
|
|
131
|
-
base_date_time = determine_base_date_time(item, field, start_timestamp)
|
|
132
|
-
new_datetime = create_new_datetime(base_date_time, parsed_time_component)
|
|
133
|
-
adjust_end_datetime(field, start_timestamp, new_datetime)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
# Performs the appropriate validation based on the field.
|
|
137
|
-
#
|
|
138
|
-
# @param options [Hash] A hash containing the parameters for validation.
|
|
139
|
-
# @option options [Array] :item The item being modified.
|
|
140
|
-
# @option options [String] :field The field being validated ('start' or 'end').
|
|
141
|
-
# @option options [Integer] :new_epoch The new time in epoch format.
|
|
142
|
-
# @option options [Integer, nil] :start_timestamp The start timestamp of the item.
|
|
143
|
-
# @option options [Integer, nil] :end_timestamp The end timestamp of the item.
|
|
144
|
-
# @option options [Time] :new_datetime The new datetime object.
|
|
145
|
-
def perform_validation(options)
|
|
146
|
-
item = options[:item]
|
|
147
|
-
field = options[:field]
|
|
148
|
-
new_epoch = options[:new_epoch]
|
|
149
|
-
start_timestamp = options[:start_timestamp]
|
|
150
|
-
end_timestamp = options[:end_timestamp]
|
|
151
|
-
new_datetime = options[:new_datetime]
|
|
152
|
-
|
|
153
|
-
validate_future_date(new_datetime)
|
|
154
|
-
|
|
155
|
-
validate_time_collisions(item, field, new_epoch) # Call the new method for both start and end
|
|
156
|
-
validate_start_time(new_epoch, end_timestamp, new_datetime) if field == 'start' && end_timestamp
|
|
157
|
-
validate_end_time(new_epoch, start_timestamp, new_datetime) if field == 'end'
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
end
|