timet 0.8.2 → 0.9.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.
@@ -12,8 +12,26 @@ module Timet
12
12
  class TimeReport
13
13
  include Formatter
14
14
 
15
- attr_reader :db, :items, :filename
15
+ # Provides access to the database instance.
16
+ attr_reader :db
16
17
 
18
+ # Provides access to the filtered items.
19
+ attr_reader :items
20
+
21
+ # Provides access to the CSV filename.
22
+ attr_reader :filename
23
+
24
+ # Initializes a new instance of the TimeReport class.
25
+ #
26
+ # @param db [Database] The database instance to use for fetching data.
27
+ # @param filter [String, nil] The filter to apply when fetching items. Possible values include 'today', 'yesterday', 'week', 'month', or a date range in the format 'YYYY-MM-DD..YYYY-MM-DD'.
28
+ # @param tag [String, nil] The tag to filter the items by.
29
+ # @param csv [String, nil] The filename to use when exporting the report to CSV.
30
+ #
31
+ # @return [void] This method does not return a value; it performs side effects such as initializing the instance variables.
32
+ #
33
+ # @example Initialize a new TimeReport instance with a filter and tag
34
+ # TimeReport.new(db, 'today', 'work', 'report.csv')
17
35
  def initialize(db, filter = nil, tag = nil, csv = nil)
18
36
  @db = db
19
37
  @filename = csv
@@ -21,6 +39,14 @@ module Timet
21
39
  @items = filter ? filter_items(@filter, tag) : @db.all_items
22
40
  end
23
41
 
42
+ # Displays the report of tracked time entries.
43
+ #
44
+ # @return [void] This method does not return a value; it performs side effects such as printing the report.
45
+ #
46
+ # @example Display the report
47
+ # time_report.display
48
+ #
49
+ # @note The method formats and prints the table header, rows, and total duration.
24
50
  def display
25
51
  return puts 'No tracked time found for the specified filter.' if items.empty?
26
52
 
@@ -33,6 +59,16 @@ module Timet
33
59
  total
34
60
  end
35
61
 
62
+ # Displays a single row of the report.
63
+ #
64
+ # @param item [Array] The item to display.
65
+ #
66
+ # @return [void] This method does not return a value; it performs side effects such as printing the row.
67
+ #
68
+ # @example Display a single row
69
+ # time_report.show_row(item)
70
+ #
71
+ # @note The method formats and prints the table header, row, and total duration.
36
72
  def show_row(item)
37
73
  format_table_header
38
74
  display_time_entry(item)
@@ -40,6 +76,14 @@ module Timet
40
76
  total
41
77
  end
42
78
 
79
+ # Exports the report to a CSV file.
80
+ #
81
+ # @return [void] This method does not return a value; it performs side effects such as writing the CSV file.
82
+ #
83
+ # @example Export the report to a CSV file
84
+ # time_report.export_sheet
85
+ #
86
+ # @note The method writes the items to a CSV file and prints a confirmation message.
43
87
  def export_sheet
44
88
  file_name = "#{filename}.csv"
45
89
  write_csv(file_name)
@@ -49,6 +93,16 @@ module Timet
49
93
 
50
94
  private
51
95
 
96
+ # Writes the items to a CSV file.
97
+ #
98
+ # @param file_name [String] The name of the CSV file to write.
99
+ #
100
+ # @return [void] This method does not return a value; it performs side effects such as writing the CSV file.
101
+ #
102
+ # @example Write items to a CSV file
103
+ # write_csv('report.csv')
104
+ #
105
+ # @note The method writes the items to the specified CSV file.
52
106
  def write_csv(file_name)
53
107
  CSV.open(file_name, 'w') do |csv|
54
108
  csv << %w[ID Start End Tag Notes]
@@ -58,6 +112,16 @@ module Timet
58
112
  end
59
113
  end
60
114
 
115
+ # Formats an item for CSV export.
116
+ #
117
+ # @param item [Array] The item to format.
118
+ #
119
+ # @return [Array] The formatted item.
120
+ #
121
+ # @example Format an item for CSV export
122
+ # format_item(item)
123
+ #
124
+ # @note The method formats the item's ID, start time, end time, tag, and notes.
61
125
  def format_item(item)
62
126
  id, start_time, end_time, tags, notes = item
63
127
  [
@@ -69,6 +133,17 @@ module Timet
69
133
  ]
70
134
  end
71
135
 
136
+ # Displays a single time entry in the report.
137
+ #
138
+ # @param item [Array] The item to display.
139
+ # @param date [String, nil] The date to display. If nil, the date is not displayed.
140
+ #
141
+ # @return [void] This method does not return a value; it performs side effects such as printing the row.
142
+ #
143
+ # @example Display a time entry
144
+ # display_time_entry(item, '2021-10-01')
145
+ #
146
+ # @note The method formats and prints the row for the time entry.
72
147
  def display_time_entry(item, date = nil)
73
148
  return puts 'Missing time entry data.' unless item
74
149
 
@@ -80,6 +155,14 @@ module Timet
80
155
  puts format_table_row(id, tag_name[0..5], start_date, start_time, end_time, duration, notes)
81
156
  end
82
157
 
158
+ # Displays the total duration of the tracked time entries.
159
+ #
160
+ # @return [void] This method does not return a value; it performs side effects such as printing the total duration.
161
+ #
162
+ # @example Display the total duration
163
+ # total
164
+ #
165
+ # @note The method calculates and prints the total duration of the tracked time entries.
83
166
  def total
84
167
  total = @items.map do |item|
85
168
  TimeHelper.calculate_duration(item[1], item[2])
@@ -88,16 +171,38 @@ module Timet
88
171
  puts format_table_separator
89
172
  end
90
173
 
174
+ # Filters the items based on the specified filter and tag.
175
+ #
176
+ # @param filter [String] The filter to apply.
177
+ # @param tag [String, nil] The tag to filter the items by.
178
+ #
179
+ # @return [Array] The filtered items.
180
+ #
181
+ # @example Filter items by date range and tag
182
+ # filter_items('2021-10-01..2021-10-31', 'work')
183
+ #
184
+ # @note The method filters the items based on the specified date range and tag.
91
185
  def filter_items(filter, tag)
92
186
  if date_ranges.key?(filter)
93
187
  start_date, end_date = date_ranges[filter]
94
188
  filter_by_date_range(start_date, end_date, tag)
189
+ elsif valid_date_format?(filter)
190
+ start_date, end_date = filter.split('..').map { |x| Date.parse(x) }
191
+ filter_by_date_range(start_date, end_date, tag)
95
192
  else
96
193
  puts 'Invalid filter. Supported filters: today, yesterday, week, month'
97
194
  []
98
195
  end
99
196
  end
100
197
 
198
+ # Provides predefined date ranges for filtering.
199
+ #
200
+ # @return [Hash] A hash containing predefined date ranges.
201
+ #
202
+ # @example Get the predefined date ranges
203
+ # date_ranges
204
+ #
205
+ # @note The method returns a hash with predefined date ranges for 'today', 'yesterday', 'week', and 'month'.
101
206
  def date_ranges
102
207
  today = Date.today
103
208
  {
@@ -108,6 +213,18 @@ module Timet
108
213
  }
109
214
  end
110
215
 
216
+ # Filters the items by date range and tag.
217
+ #
218
+ # @param start_date [Date] The start date of the range.
219
+ # @param end_date [Date, nil] The end date of the range. If nil, the end date is the start date + 1 day.
220
+ # @param tag [String, nil] The tag to filter the items by.
221
+ #
222
+ # @return [Array] The filtered items.
223
+ #
224
+ # @example Filter items by date range and tag
225
+ # filter_by_date_range(Date.new(2021, 10, 1), Date.new(2021, 10, 31), 'work')
226
+ #
227
+ # @note The method filters the items based on the specified date range and tag.
111
228
  def filter_by_date_range(start_date, end_date = nil, tag = nil)
112
229
  start_time = TimeHelper.date_to_timestamp(start_date)
113
230
  end_time = TimeHelper.calculate_end_time(start_date, end_date)
@@ -117,13 +234,48 @@ module Timet
117
234
  )
118
235
  end
119
236
 
237
+ # Formats the filter string.
238
+ #
239
+ # @param filter [String, nil] The filter string to format.
240
+ #
241
+ # @return [String] The formatted filter string.
242
+ #
243
+ # @example Format the filter string
244
+ # formatted_filter('t') # => 'today'
245
+ #
246
+ # @note The method maps shorthand filters to their full names and validates date formats.
120
247
  def formatted_filter(filter)
121
- return 'today' if %w[today t].include?(filter)
122
- return 'yesterday' if %w[yesterday y].include?(filter)
123
- return 'week' if %w[week w].include?(filter)
124
- return 'month' if %w[month m].include?(filter)
248
+ filter_map = {
249
+ 'today' => %w[today t],
250
+ 'yesterday' => %w[yesterday y],
251
+ 'week' => %w[week w],
252
+ 'month' => %w[month m]
253
+ }
254
+
255
+ filter_map.each do |key, values|
256
+ return key if values.include?(filter)
257
+ end
258
+
259
+ return filter if filter && valid_date_format?(filter)
125
260
 
126
261
  'today'
127
262
  end
263
+
264
+ # Validates the date format.
265
+ #
266
+ # @param date_string [String] The date string to validate.
267
+ #
268
+ # @return [Boolean] True if the date format is valid, otherwise false.
269
+ #
270
+ # @example Validate the date format
271
+ # valid_date_format?('2021-10-01') # => true
272
+ #
273
+ # @note The method validates the date format for single dates and date ranges.
274
+ def valid_date_format?(date_string)
275
+ date_format_single = /^\d{4}-\d{2}-\d{2}$/
276
+ date_format_range = /^\d{4}-\d{2}-\d{2}\.\.\d{4}-\d{2}-\d{2}$/
277
+
278
+ date_string.match?(date_format_single) || date_string.match?(date_format_range)
279
+ end
128
280
  end
129
281
  end
@@ -6,8 +6,23 @@ module Timet
6
6
  # If the field is 'start' or 'end', it checks and updates the value accordingly.
7
7
  # Otherwise, it directly updates the field with the new value.
8
8
  module ValidationEditHelper
9
+ # Constants for time fields.
9
10
  TIME_FIELDS = %w[start end].freeze
10
11
 
12
+ # Validates and updates a tracking item's field with a new value.
13
+ #
14
+ # @param item [Array] The tracking item to be updated.
15
+ # @param field [String] The field to be updated.
16
+ # @param new_value [String, nil] The new value to be set for the specified field.
17
+ #
18
+ # @return [Array, nil] The updated tracking item if the update was successful, otherwise nil.
19
+ #
20
+ # @example Validate and update the 'notes' field of a tracking item
21
+ # validate_and_update(item, 'notes', 'Updated notes')
22
+ #
23
+ # @note The method checks if the field is a time field (start or end) and processes it accordingly.
24
+ # @note If the field is not a time field, it directly updates the field with the new value.
25
+ # @note The method returns the updated tracking item if the update was successful.
11
26
  def validate_and_update(item, field, new_value)
12
27
  return if new_value.nil?
13
28
 
@@ -18,10 +33,24 @@ module Timet
18
33
  else
19
34
  @db.update_item(id, field, new_value)
20
35
  end
36
+
37
+ @db.find_item(id)
21
38
  end
22
39
 
23
40
  private
24
41
 
42
+ # Processes and updates a time field (start or end) of a tracking item.
43
+ #
44
+ # @param item [Array] The tracking item to be updated.
45
+ # @param field [String] The time field to be updated.
46
+ # @param date_value [String] The new value for the time field.
47
+ # @param id [Integer] The ID of the tracking item.
48
+ #
49
+ # @return [void] This method does not return a value; it performs side effects such as updating the time field.
50
+ #
51
+ # @note The method formats the date value and checks if it is valid.
52
+ # @note If the date value is valid, it updates the time field with the new value.
53
+ # @note If the date value is invalid, it prints an error message.
25
54
  def process_and_update_time_field(item, field, date_value, id)
26
55
  formatted_date = TimeHelper.format_time_string(date_value)
27
56
 
@@ -37,10 +66,28 @@ module Timet
37
66
  end
38
67
  end
39
68
 
69
+ # Prints an error message for an invalid date.
70
+ #
71
+ # @param message [String] The error message to be printed.
72
+ #
73
+ # @return [void] This method does not return a value; it performs side effects such as printing an error message.
74
+ #
75
+ # @example Print an error message for an invalid date
76
+ # print_error('Invalid date: 2023-13-32')
40
77
  def print_error(message)
41
78
  puts "\u001b[31mInvalid date: #{message}\033[0m"
42
79
  end
43
80
 
81
+ # Updates a time field (start or end) of a tracking item with a formatted date value.
82
+ #
83
+ # @param item [Array] The tracking item to be updated.
84
+ # @param field [String] The time field to be updated.
85
+ # @param formatted_value [String] The formatted date value.
86
+ #
87
+ # @return [Time] The updated time value.
88
+ #
89
+ # @example Update the 'start' field of a tracking item with a formatted date value
90
+ # update_time_field(item, 'start', '2023-10-01 12:00:00')
44
91
  def update_time_field(item, field, formatted_value)
45
92
  field_index = Timet::Application::FIELD_INDEX[field]
46
93
  timestamp = item[field_index]
@@ -49,6 +96,17 @@ module Timet
49
96
  DateTime.strptime(current_time.join(' '), '%Y-%m-%d %H:%M:%S %z').to_time
50
97
  end
51
98
 
99
+ # Validates if a new time value is valid for a specific time field (start or end).
100
+ #
101
+ # @param item [Array] The tracking item to be validated.
102
+ # @param field [String] The time field to be validated.
103
+ # @param new_value_epoch [Integer] The new time value in epoch format.
104
+ # @param id [Integer] The ID of the tracking item.
105
+ #
106
+ # @return [Boolean] Returns true if the new time value is valid, otherwise false.
107
+ #
108
+ # @example Validate a new 'start' time value
109
+ # valid_time_value?(item, 'start', 1633072800, 1)
52
110
  def valid_time_value?(item, field, new_value_epoch, id)
53
111
  item_start = fetch_item_start(item)
54
112
  item_end = fetch_item_end(item)
@@ -62,18 +120,51 @@ module Timet
62
120
  end
63
121
  end
64
122
 
123
+ # Fetches the start time of a tracking item.
124
+ #
125
+ # @param item [Array] The tracking item.
126
+ #
127
+ # @return [Integer] The start time in epoch format.
128
+ #
129
+ # @example Fetch the start time of a tracking item
130
+ # fetch_item_start(item)
65
131
  def fetch_item_start(item)
66
132
  item[Timet::Application::FIELD_INDEX['start']]
67
133
  end
68
134
 
135
+ # Fetches the end time of a tracking item.
136
+ #
137
+ # @param item [Array] The tracking item.
138
+ #
139
+ # @return [Integer] The end time in epoch format.
140
+ #
141
+ # @example Fetch the end time of a tracking item
142
+ # fetch_item_end(item)
69
143
  def fetch_item_end(item)
70
144
  item[Timet::Application::FIELD_INDEX['end']] || TimeHelper.current_timestamp
71
145
  end
72
146
 
147
+ # Fetches the end time of the tracking item before the current one.
148
+ #
149
+ # @param id [Integer] The ID of the current tracking item.
150
+ # @param item_start [Integer] The start time of the current tracking item.
151
+ #
152
+ # @return [Integer] The end time of the previous tracking item in epoch format.
153
+ #
154
+ # @example Fetch the end time of the previous tracking item
155
+ # fetch_item_before_end(1, 1633072800)
73
156
  def fetch_item_before_end(id, item_start)
74
157
  @db.find_item(id - 1)&.dig(Timet::Application::FIELD_INDEX['end']) || item_start
75
158
  end
76
159
 
160
+ # Fetches the start time of the tracking item after the current one.
161
+ #
162
+ # @param id [Integer] The ID of the current tracking item.
163
+ #
164
+ # @return [Integer] The start time of the next tracking item in epoch format.
165
+ #
166
+ # @example Fetch the start time of the next tracking item
167
+ # fetch_item_after_start(1)
77
168
  def fetch_item_after_start(id)
78
169
  @db.find_item(id + 1)&.dig(Timet::Application::FIELD_INDEX['start']) || TimeHelper.current_timestamp
79
170
  end
data/lib/timet/version.rb CHANGED
@@ -1,5 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Timet
4
- VERSION = '0.8.2'
4
+ # The version of the Timet application.
5
+ #
6
+ # @return [String] The version number in the format 'major.minor.patch'.
7
+ #
8
+ # @example Get the version of the Timet application
9
+ # Timet::VERSION # => '0.9.1'
10
+ VERSION = '0.9.1'
5
11
  end
data/lib/timet.rb CHANGED
@@ -1,5 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Require the Timet::Application class from the 'timet/application' file.
4
+ #
5
+ # @note This statement loads the Timet::Application class, which is responsible for handling the command-line interface and user commands.
3
6
  require_relative 'timet/application'
7
+
8
+ # Require the Timet::Database class from the 'timet/database' file.
9
+ #
10
+ # @note This statement loads the Timet::Database class, which provides database access for managing time tracking data.
4
11
  require_relative 'timet/database'
12
+
13
+ # Require the Timet::TimeReport class from the 'timet/time_report' file.
14
+ #
15
+ # @note This statement loads the Timet::TimeReport class, which is responsible for displaying a report of tracked time entries.
5
16
  require_relative 'timet/time_report'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frank Vielma
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-02 00:00:00.000000000 Z
11
+ date: 2024-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -93,6 +93,7 @@ metadata:
93
93
  homepage_uri: https://frankvielma.github.io/posts/timet-a-powerful-command-line-tool-for-tracking-your-time/
94
94
  source_code_uri: https://github.com/frankvielma/timet
95
95
  changelog_uri: https://github.com/frankvielma/timet/blob/main/CHANGELOG.md
96
+ documentation_uri: https://rubydoc.info/gems/timet
96
97
  rubygems_mfa_required: 'true'
97
98
  post_install_message:
98
99
  rdoc_options: []