timet 1.2.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3f1c37d739d4c6712cf21af39357a45ded45b8a63d8d7e9932ac859fe72b000
4
- data.tar.gz: 20bf38e7554d3eb1326d1d7e2ae1095792c9b20a473d383f9f123e2ac0bbd2b2
3
+ metadata.gz: db3c3e635b08a071bc271be5788a4b689f0a28203dea81c2dc73a78c312b089e
4
+ data.tar.gz: 2c5c831731c0ec619064653b8239381a53e5c973f92f347e98a74de8c07547e0
5
5
  SHA512:
6
- metadata.gz: 92892c05063d444a713eaafec9eb63ff970a4926364b594dd31b61f229e9b415d3a171da61ffaa96660512708076423348ece7f120c0e21e075aa777e8e5b1b4
7
- data.tar.gz: 3e1799d361eb28b500ce81740eb9ee1f16d55048e9425203aa6482265a9bc1d891e6d6897c626b454b2a18854523b99ed4acb0edf3a63e0dc043f16d1ebf4b04
6
+ metadata.gz: 0b98ea65edc254804e58c16ce0f200209ee57b9b3629fc182ec822c1b359380015bdaf281db64a9bd96acaa5ded45489f4464602ae90ab3e11916556e879e88a
7
+ data.tar.gz: fba083a2484a17a3af3efb5c0d2dcad4b08f463d162b6689c4a6445948d6f2bd517ef0b3efd794d0b0b05a818799798446a65902cf97ebe94519f884aa2c9927
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.2.1] - 2024-10-18
4
+
5
+ **Improvements:**
6
+ - Updated the time block chart formatting to use square brackets for better visual representation.
7
+ - Refactored the `play_sound_and_notify` method to avoid redundant platform checks and introduced platform-specific session runners.
8
+ - Improved readability and maintainability of the `format_tag_distribution` method by extracting logic into a new private method.
9
+ - Updated the `rubocop` gem from `~> 1.65` to `~> 1.67`.
10
+
11
+ ### Bug fixes:
12
+ - Fixed a `NoMethodError` caused by an undefined method `process_and_print_tags` in the `format_tag_distribution` method.
13
+ - Fixed line length violations in several files to comply with `rubocop` rules.
14
+
15
+ ### Additional Considerations:
16
+ - The changes in this pull request should be thoroughly tested to ensure that they do not introduce any regressions.
17
+ - Future improvements could include further refactoring to extract more logic into separate methods or classes, depending on the complexity and requirements of the application.
18
+
3
19
  ## [1.2.0] - 2024-10-11
4
20
 
5
21
  **Improvements:**
@@ -27,12 +43,32 @@
27
43
  - Integrated new methods into `time_report` to enhance time tracking visualization.
28
44
  - Updated the version number in `lib/timet/version.rb` to 1.2.0.
29
45
 
30
- ### Additional Considerations:
46
+ **Additional Considerations:**
31
47
 
32
48
  - The enhancements made in this pull request aim to improve the user experience and provide more powerful visualization and reporting capabilities for time tracking.
33
49
  - Reviewers are encouraged to test the new visualization methods and provide feedback on their effectiveness and usability.
34
50
  - The README updates should make it easier for new users to understand and use the `timet` tool.
35
51
 
52
+ ## [1.1.0] - 2024-10-09
53
+
54
+ **Improvements:**
55
+
56
+ - Added a new `version` command to display the current version of the Timet gem.
57
+ - Introduced an alias `tt` for the `timet` command, providing a shorter alternative.
58
+ - Updated the README to include the `tt` alias and provide examples for both `timet` and `tt` commands.
59
+ - Updated the gem version to `1.1.0`.
60
+ - Added the `tt` executable to the gemspec.
61
+ - Updated the `rspec-mocks` dependency to version `3.13.2`.
62
+
63
+ **Tasks:**
64
+
65
+ - Update `Gemfile.lock` to reflect the new gem version and updated dependencies.
66
+ - Add the `tt` executable script to the `bin` directory.
67
+ - Update the `version` command in `lib/timet/application.rb` with Yardoc documentation.
68
+ - Update the `VERSION` constant in `lib/timet/version.rb` to `1.1.0`.
69
+ - Update the `timet.gemspec` to include the `tt` executable.
70
+ - Update the README to reflect the new `tt` alias and provide examples for both `timet` and `tt` commands.
71
+
36
72
  ## [1.0.0] - 2024-10-07
37
73
 
38
74
  **Improvements:**
data/README.md CHANGED
@@ -35,8 +35,8 @@ Tracked time report [today]:
35
35
  | Total: | 02:00:00 | |
36
36
  +-------+------------+--------+----------+----------+----------+--------------------------+
37
37
 
38
- ⏳ ↦ 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
39
- ▂▂ ▇▇ ▅▅ ▄▄
38
+ ⏳ ↦ [ 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ]
39
+ [ ▂▂ ▇▇ ▅▅ ▄▄ ]
40
40
 
41
41
  Tag8: 50.0% ▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅
42
42
  Tag3: 50.0% ▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅
@@ -45,10 +45,13 @@ module Timet
45
45
  # trigger a sound and notification after the specified time has elapsed.
46
46
  #
47
47
  # @param tag [String] The tag associated with the tracking session. This is a required parameter.
48
- # @param notes [String, nil] Optional notes to be associated with the tracking session. If not provided, it defaults to the value in `options[:notes]`.
49
- # @param pomodoro [Numeric, nil] Optional Pomodoro time in minutes. If not provided, it defaults to the value in `options[:pomodoro]`.
48
+ # @param notes [String, nil] Optional notes to be associated with the tracking session. If not provided, it
49
+ # defaults to the value in `options[:notes]`.
50
+ # @param pomodoro [Numeric, nil] Optional Pomodoro time in minutes. If not provided, it defaults to the value in
51
+ # `options[:pomodoro]`.
50
52
  #
51
- # @return [void] This method does not return a value; it performs side effects such as inserting a tracking item, playing a sound, sending a notification, and generating a summary.
53
+ # @return [void] This method does not return a value; it performs side effects such as inserting a tracking item,
54
+ # playing a sound, sending a notification, and generating a summary.
52
55
  #
53
56
  # @example Start a tracking session with a tag and notes
54
57
  # start('work', 'Starting work on project X', 25)
@@ -74,14 +77,17 @@ module Timet
74
77
  desc 'stop', 'stop time tracking'
75
78
  # Stops the current tracking session if there is one in progress.
76
79
  #
77
- # @return [void] This method does not return a value; it performs side effects such as updating the tracking item and generating a summary.
80
+ # @return [void] This method does not return a value; it performs side effects such as updating the tracking item
81
+ # and generating a summary.
78
82
  #
79
83
  # @example Stop the current tracking session
80
84
  # stop
81
85
  #
82
86
  # @note The method checks if the last tracking item is in progress by calling `@db.last_item_status`.
83
- # @note If the last item is in progress, it fetches the last item's ID using `@db.fetch_last_id` and updates it with the current timestamp.
84
- # @note The method then fetches the last item using `@db.last_item` and generates a summary if the result is not nil.
87
+ # @note If the last item is in progress, it fetches the last item's ID using `@db.fetch_last_id` and updates it
88
+ # with the current timestamp.
89
+ # @note The method then fetches the last item using `@db.last_item` and generates a summary if the result
90
+ # is not nil.
85
91
  def stop(display = nil)
86
92
  return unless @db.last_item_status == :in_progress
87
93
 
@@ -94,14 +100,16 @@ module Timet
94
100
  desc 'resume (r)', 'resume last task'
95
101
  # Resumes the last tracking session if it was completed.
96
102
  #
97
- # @return [void] This method does not return a value; it performs side effects such as resuming a tracking session or providing feedback.
103
+ # @return [void] This method does not return a value; it performs side effects such as resuming a tracking session
104
+ # or providing feedback.
98
105
  #
99
106
  # @example Resume the last tracking session
100
107
  # resume
101
108
  #
102
109
  # @note The method checks the status of the last tracking item using `@db.last_item_status`.
103
110
  # @note If the last item is in progress, it prints a message indicating that a task is currently being tracked.
104
- # @note If the last item is complete, it fetches the last item using `@db.last_item`, retrieves the tag and notes, and calls the `start` method to resume the tracking session.
111
+ # @note If the last item is complete, it fetches the last item using `@db.last_item`, retrieves the tag and notes,
112
+ # and calls the `start` method to resume the tracking session.
105
113
  def resume
106
114
  status = @db.last_item_status
107
115
 
@@ -121,12 +129,15 @@ module Timet
121
129
  desc 'summary (su) [filter] [tag] --csv=csv_filename',
122
130
  ' [filter] => [today (t), yesterday (y), week (w), month (m), [start_date]..[end_date]] [tag]'
123
131
  option :csv, type: :string, desc: 'Export to CSV file'
124
- # Generates a summary of tracking items based on the provided filter and tag, and optionally exports the summary to a CSV file.
132
+ # Generates a summary of tracking items based on the provided filter and tag, and optionally exports the summary
133
+ # to a CSV file.
125
134
  #
126
- # @param filter [String, nil] The filter to apply when generating the summary. Possible values include 'today', 'yesterday', 'week', 'month', or a date range in the format '[start_date]..[end_date]'.
135
+ # @param filter [String, nil] The filter to apply when generating the summary. Possible values include 'today',
136
+ # 'yesterday', 'week', 'month', or a date range in the format '[start_date]..[end_date]'.
127
137
  # @param tag [String, nil] The tag to filter the tracking items by.
128
138
  #
129
- # @return [void] This method does not return a value; it performs side effects such as displaying the summary and exporting to CSV if specified.
139
+ # @return [void] This method does not return a value; it performs side effects such as displaying the summary and
140
+ # exporting to CSV if specified.
130
141
  #
131
142
  # @example Generate a summary for today
132
143
  # summary('today')
@@ -139,7 +150,8 @@ module Timet
139
150
  #
140
151
  # @note The method initializes a `TimeReport` object with the database, filter, tag, and optional CSV filename.
141
152
  # @note The method calls `display` on the `TimeReport` object to show the summary.
142
- # @note If a CSV filename is provided and there are items to export, the method calls `export_sheet` to export the summary to a CSV file.
153
+ # @note If a CSV filename is provided and there are items to export, the method calls `export_sheet` to export the
154
+ # summary to a CSV file.
143
155
  # @note If no items are found to export, it prints a message indicating that no items were found.
144
156
  def summary(filter = nil, tag = nil)
145
157
  csv_filename = options[:csv]&.split('.')&.first
@@ -156,13 +168,17 @@ module Timet
156
168
 
157
169
  desc 'edit (e) [id] [field] [value]',
158
170
  'edit a task, [field] (notes, tag, start or end) and [value] are optional parameters'
159
- # Edits a specific tracking item by its ID, allowing the user to modify fields such as notes, tag, start time, or end time.
171
+ # Edits a specific tracking item by its ID, allowing the user to modify fields such as notes, tag, start time, or
172
+ # end time.
160
173
  #
161
174
  # @param id [Integer] The ID of the tracking item to be edited.
162
- # @param field [String, nil] The field to be edited. Possible values include 'notes', 'tag', 'start', or 'end'. If not provided, the user will be prompted to select a field.
163
- # @param new_value [String, nil] The new value to be set for the specified field. If not provided, the user will be prompted to enter a new value.
175
+ # @param field [String, nil] The field to be edited. Possible values include 'notes', 'tag', 'start', or 'end'.
176
+ # If not provided, the user will be prompted to select a field.
177
+ # @param new_value [String, nil] The new value to be set for the specified field. If not provided, the user will be
178
+ # prompted to enter a new value.
164
179
  #
165
- # @return [void] This method does not return a value; it performs side effects such as updating the tracking item and displaying the updated item.
180
+ # @return [void] This method does not return a value; it performs side effects such as updating the tracking item
181
+ # and displaying the updated item.
166
182
  #
167
183
  # @example Edit the notes of a tracking item with ID 1
168
184
  # edit(1, 'notes', 'Updated notes')
@@ -172,7 +188,8 @@ module Timet
172
188
  #
173
189
  # @note The method first attempts to find the tracking item by its ID using `@db.find_item(id)`.
174
190
  # @note If the item is found, it displays the current item details using `display_item(item)`.
175
- # @note If the field or new value is not provided, the user is prompted to select a field to edit and enter a new value.
191
+ # @note If the field or new value is not provided, the user is prompted to select a field to edit and enter
192
+ # a new value.
176
193
  # @note The method then validates and updates the item using `validate_and_update(item, field, new_value)`.
177
194
  # @note Finally, it displays the updated item details using `display_item(updated_item)`.
178
195
  def edit(id, field = nil, new_value = nil)
@@ -194,15 +211,18 @@ module Timet
194
211
  #
195
212
  # @param id [Integer] The ID of the tracking item to be deleted.
196
213
  #
197
- # @return [void] This method does not return a value; it performs side effects such as deleting the tracking item and displaying a confirmation message.
214
+ # @return [void] This method does not return a value; it performs side effects such as deleting the tracking item
215
+ # and displaying a confirmation message.
198
216
  #
199
217
  # @example Delete a tracking item with ID 1
200
218
  # delete(1)
201
219
  #
202
220
  # @note The method first attempts to find the tracking item by its ID using `@db.find_item(id)`.
203
221
  # @note If the item is found, it displays the item details using `TimeReport.new(@db).show_row(item)`.
204
- # @note The method then prompts the user for confirmation using `TTY::Prompt.new.yes?('Are you sure you want to delete this entry?')`.
205
- # @note If the user confirms, the method deletes the item and prints a confirmation message using `delete_item_and_print_message(id, "Deleted #{id}")`.
222
+ # @note The method then prompts the user for confirmation using `TTY::Prompt.new.yes?('Are you sure you want
223
+ # to delete this entry?')`.
224
+ # @note If the user confirms, the method deletes the item and prints a confirmation message using
225
+ # `delete_item_and_print_message(id, "Deleted #{id}")`.
206
226
  def delete(id)
207
227
  item = @db.find_item(id)
208
228
  return puts "No tracked time found for id: #{id}" unless item
@@ -216,14 +236,16 @@ module Timet
216
236
  desc 'cancel (c)', 'cancel active time tracking'
217
237
  # Cancels the active time tracking session by deleting the last tracking item.
218
238
  #
219
- # @return [void] This method does not return a value; it performs side effects such as deleting the active tracking item and displaying a confirmation message.
239
+ # @return [void] This method does not return a value; it performs side effects such as deleting the active tracking
240
+ # item and displaying a confirmation message.
220
241
  #
221
242
  # @example Cancel the active time tracking session
222
243
  # cancel
223
244
  #
224
245
  # @note The method fetches the ID of the last tracking item using `@db.fetch_last_id`.
225
246
  # @note It checks if the last item is in progress by comparing `@db.last_item_status` with `:complete`.
226
- # @note If the last item is in progress, it deletes the item and prints a confirmation message using `delete_item_and_print_message(id, "Canceled active time tracking #{id}")`.
247
+ # @note If the last item is in progress, it deletes the item and prints a confirmation message using
248
+ # `delete_item_and_print_message(id, "Canceled active time tracking #{id}")`.
227
249
  # @note If there is no active time tracking, it prints a message indicating that there is no active time tracking.
228
250
  def cancel
229
251
  id = @db.fetch_last_id
@@ -240,7 +262,8 @@ module Timet
240
262
  # MyClass.exit_on_failure? # => true
241
263
  #
242
264
  # @note This method is typically used in command-line applications to control the behavior when a command fails.
243
- # @note Returning `true` means that the application will exit immediately if a command fails, which is useful for ensuring that errors are handled gracefully.
265
+ # @note Returning `true` means that the application will exit immediately if a command fails, which is useful for
266
+ # ensuring that errors are handled gracefully.
244
267
  def self.exit_on_failure?
245
268
  true
246
269
  end
@@ -264,7 +287,8 @@ module Timet
264
287
  # @param id [Integer] The ID of the tracking item to be deleted.
265
288
  # @param message [String] The message to be printed after the item is deleted.
266
289
  #
267
- # @return [void] This method does not return a value; it performs side effects such as deleting the tracking item and printing a message.
290
+ # @return [void] This method does not return a value; it performs side effects such as deleting the tracking item
291
+ # and printing a message.
268
292
  #
269
293
  # @example Delete a tracking item with ID 1 and print a confirmation message
270
294
  # delete_item_and_print_message(1, 'Deleted item 1')
@@ -12,7 +12,8 @@ module Timet
12
12
  # @example Display the details of a tracking item
13
13
  # display_item(item)
14
14
  #
15
- # @note The method initializes a `TimeReport` object with the database and calls `show_row` to display the item details.
15
+ # @note The method initializes a `TimeReport` object with the database and calls `show_row` to display the
16
+ # item details.
16
17
  def display_item(item)
17
18
  TimeReport.new(@db).show_row(item)
18
19
  end
@@ -28,7 +29,8 @@ module Timet
28
29
  # prompt_for_new_value(item, 'notes')
29
30
  #
30
31
  # @note The method retrieves the current value of the field using `field_value`.
31
- # @note The method uses `TTY::Prompt.new` to prompt the user for a new value, displaying the current value in the prompt.
32
+ # @note The method uses `TTY::Prompt.new` to prompt the user for a new value, displaying the current value
33
+ # in the prompt.
32
34
  def prompt_for_new_value(item, field)
33
35
  current_value = field_value(item, field)
34
36
  prompt = TTY::Prompt.new(active_color: :green)
@@ -54,13 +56,15 @@ module Timet
54
56
  # @param item [Hash] The tracking item.
55
57
  # @param field [String] The field to retrieve the value for.
56
58
  #
57
- # @return [String, Time] The value of the specified field. If the field is 'start' or 'end', it returns the value as a Time object.
59
+ # @return [String, Time] The value of the specified field. If the field is 'start' or 'end', it returns the value
60
+ # as a Time object.
58
61
  #
59
62
  # @example Retrieve the value of the 'notes' field
60
63
  # field_value(item, 'notes')
61
64
  #
62
65
  # @note The method retrieves the index of the field from `Timet::Application::FIELD_INDEX`.
63
- # @note If the field is 'start' or 'end', the method converts the value to a Time object using `TimeHelper.timestamp_to_time`.
66
+ # @note If the field is 'start' or 'end', the method converts the value to a Time object
67
+ # using `TimeHelper.timestamp_to_time`.
64
68
  def field_value(item, field)
65
69
  index = Timet::Application::FIELD_INDEX[field]
66
70
  value = item[index]
@@ -89,15 +93,38 @@ module Timet
89
93
  #
90
94
  # @return [void]
91
95
  def play_sound_and_notify(time, tag)
92
- if RUBY_PLATFORM.downcase.include?('linux')
93
- pid = spawn("sleep #{time} && tput bel && /home/frank/Software/frankvielma/gems/timet/bin/timet stop 0 && notify-send --icon=clock 'Pomodoro session complete! (tag: #{tag}) Time for a break.' &")
94
- Process.wait(pid)
95
- elsif RUBY_PLATFORM.downcase.include?('darwin')
96
- pid = spawn("(sleep #{time} && afplay /System/Library/Sounds/Basso.aiff && osascript -e 'display notification \"Pomodoro session complete! Time for a break.\"') &")
97
- Process.wait(pid)
96
+ platform = RUBY_PLATFORM.downcase
97
+ if platform.include?('linux')
98
+ run_linux_session(time, tag)
99
+ elsif platform.include?('darwin')
100
+ run_mac_session(time, tag)
98
101
  else
99
102
  puts 'Unsupported operating system'
100
103
  end
101
104
  end
105
+
106
+ # Runs a Pomodoro session on a Linux system.
107
+ #
108
+ # @param time [Integer] The duration of the Pomodoro session in seconds.
109
+ # @param tag [String] A tag or label for the session, used in the notification message.
110
+ # @return [void]
111
+ def run_linux_session(time, tag)
112
+ notification_command = "notify-send --icon=clock 'Pomodoro session complete! (tag: #{tag}) Time for a break.'"
113
+ command = "sleep #{time} && tput bel && tt stop 0 && #{notification_command} &"
114
+ pid = spawn(command)
115
+ Process.wait(pid)
116
+ end
117
+
118
+ # Runs a Pomodoro session on a macOS system.
119
+ #
120
+ # @param time [Integer] The duration of the Pomodoro session in seconds.
121
+ # @param _tag [String] A tag or label for the session, not used in the notification message on macOS.
122
+ # @return [void]
123
+ def run_mac_session(time, _tag)
124
+ notification_command = "osascript -e 'display notification \"Pomodoro session complete! Time for a break.\"'"
125
+ command = "sleep #{time} && afplay /System/Library/Sounds/Basso.aiff && tt stop 0 && #{notification_command} &"
126
+ pid = spawn(command)
127
+ Process.wait(pid)
128
+ end
102
129
  end
103
130
  end
@@ -14,7 +14,8 @@ module Timet
14
14
  #
15
15
  # @param database_path [String] The path to the SQLite database file. Defaults to DEFAULT_DATABASE_PATH.
16
16
  #
17
- # @return [void] This method does not return a value; it performs side effects such as initializing the database connection and creating the necessary tables.
17
+ # @return [void] This method does not return a value; it performs side effects such as initializing the database
18
+ # connection and creating the necessary tables.
18
19
  #
19
20
  # @example Initialize a new Database instance with the default path
20
21
  # Database.new
@@ -22,7 +23,8 @@ module Timet
22
23
  # @example Initialize a new Database instance with a custom path
23
24
  # Database.new('/path/to/custom.db')
24
25
  #
25
- # @note The method creates a new SQLite3 database connection and initializes the necessary tables if they do not already exist.
26
+ # @note The method creates a new SQLite3 database connection and initializes the necessary tables if they
27
+ # do not already exist.
26
28
  def initialize(database_path = DEFAULT_DATABASE_PATH)
27
29
  @db = SQLite3::Database.new(database_path)
28
30
  create_table
@@ -31,7 +33,8 @@ module Timet
31
33
 
32
34
  # Creates the items table if it doesn't already exist.
33
35
  #
34
- # @return [void] This method does not return a value; it performs side effects such as executing SQL to create the table.
36
+ # @return [void] This method does not return a value; it performs side effects such as executing SQL to
37
+ # create the table.
35
38
  #
36
39
  # @example Create the items table
37
40
  # create_table
@@ -50,7 +53,8 @@ module Timet
50
53
 
51
54
  # Adds a new column named "notes" to the "items" table if it doesn't exist.
52
55
  #
53
- # @return [void] This method does not return a value; it performs side effects such as executing SQL to add the column.
56
+ # @return [void] This method does not return a value; it performs side effects such as executing SQL to add
57
+ # the column.
54
58
  #
55
59
  # @example Add the notes column to the items table
56
60
  # add_notes
@@ -73,7 +77,8 @@ module Timet
73
77
  # @param tag [String] The tag associated with the item.
74
78
  # @param notes [String] The notes associated with the item.
75
79
  #
76
- # @return [void] This method does not return a value; it performs side effects such as executing SQL to insert the item.
80
+ # @return [void] This method does not return a value; it performs side effects such as executing SQL
81
+ # to insert the item.
77
82
  #
78
83
  # @example Insert a new item into the items table
79
84
  # insert_item(1633072800, 'work', 'Completed task X')
@@ -89,7 +94,8 @@ module Timet
89
94
  # @param field [String] The field to be updated.
90
95
  # @param value [String, Integer, nil] The new value for the specified field.
91
96
  #
92
- # @return [void] This method does not return a value; it performs side effects such as executing SQL to update the item.
97
+ # @return [void] This method does not return a value; it performs side effects such as executing SQL
98
+ # to update the item.
93
99
  #
94
100
  # @example Update the tag of an item with ID 1
95
101
  # update_item(1, 'tag', 'updated_work')
@@ -105,7 +111,8 @@ module Timet
105
111
  #
106
112
  # @param id [Integer] The ID of the item to be deleted.
107
113
  #
108
- # @return [void] This method does not return a value; it performs side effects such as executing SQL to delete the item.
114
+ # @return [void] This method does not return a value; it performs side effects such as executing SQL
115
+ # to delete the item.
109
116
  #
110
117
  # @example Delete an item with ID 1
111
118
  # delete_item(1)
@@ -174,7 +181,8 @@ module Timet
174
181
  # @example Fetch all items from today
175
182
  # all_items
176
183
  #
177
- # @note The method executes SQL to fetch all items from the 'items' table that have a start time greater than or equal to today.
184
+ # @note The method executes SQL to fetch all items from the 'items' table that have a start time greater than
185
+ # or equal to today.
178
186
  def all_items
179
187
  execute_sql("SELECT * FROM items where start >= '#{Date.today.to_time.to_i}' ORDER BY id DESC")
180
188
  end
@@ -199,7 +207,8 @@ module Timet
199
207
 
200
208
  # Closes the database connection.
201
209
  #
202
- # @return [void] This method does not return a value; it performs side effects such as closing the database connection.
210
+ # @return [void] This method does not return a value; it performs side effects such as closing the
211
+ # database connection.
203
212
  #
204
213
  # @example Close the database connection
205
214
  # close
@@ -218,7 +227,8 @@ module Timet
218
227
  # @example Convert 3661 seconds to HH:MM:SS format
219
228
  # seconds_to_hms(3661) # => '01:01:01'
220
229
  #
221
- # @note The method converts the given number of seconds into hours, minutes, and seconds, and formats them as HH:MM:SS.
230
+ # @note The method converts the given number of seconds into hours, minutes, and seconds, and formats
231
+ # them as HH:MM:SS.
222
232
  def seconds_to_hms(seconds)
223
233
  hours, remainder = seconds.divmod(3600)
224
234
  minutes, seconds = remainder.divmod(60)
@@ -6,7 +6,8 @@ module Timet
6
6
  module Formatter
7
7
  # Formats the header of the time tracking report table.
8
8
  #
9
- # @return [void] This method does not return a value; it performs side effects such as printing the formatted header.
9
+ # @return [void] This method does not return a value; it performs side effects such as printing
10
+ # the formatted header.
10
11
  #
11
12
  # @example Format and print the table header
12
13
  # format_table_header
@@ -16,7 +17,7 @@ module Timet
16
17
  header = <<~TABLE
17
18
  Tracked time report \e[5m\u001b[31m[#{@filter}]\033[0m:
18
19
  #{format_table_separator}
19
- \033[32m| Id | Date | Tag | Start | End | Duration | Notes |\033[0m
20
+ \033[32m| Id | Date | Tag | Start | End | Duration | Notes |\033[0m
20
21
  #{format_table_separator}
21
22
  TABLE
22
23
  puts header
@@ -27,11 +28,11 @@ module Timet
27
28
  # @return [String] The formatted separator line.
28
29
  #
29
30
  # @example Get the formatted table separator
30
- # format_table_separator # => '+-------+------------+--------+----------+----------+----------+--------------------------+'
31
+ # format_table_separator # => '+-------+------------+--------+----------+----------+----------+------------+'
31
32
  #
32
33
  # @note The method returns a string representing the separator line for the table.
33
34
  def format_table_separator
34
- '+-------+------------+--------+----------+----------+----------+--------------------------+'
35
+ '+-------+------------+--------+----------+----------+----------+--------------------+'
35
36
  end
36
37
 
37
38
  # Formats a row of the time tracking report table.
@@ -59,10 +60,11 @@ module Timet
59
60
  #
60
61
  # @note The method truncates the notes to a maximum of 20 characters and pads them to a fixed width.
61
62
  def format_notes(notes)
62
- return ' ' * 23 if notes.nil?
63
+ spaces = 17
64
+ return ' ' * spaces if notes.nil?
63
65
 
64
- notes = "#{notes.slice(0, 20)}..." if notes.length > 20
65
- notes.ljust(23)
66
+ notes = "#{notes.slice(0, spaces - 3)}..." if notes.length > spaces - 3
67
+ notes.ljust(spaces)
66
68
  end
67
69
 
68
70
  # @!method format_tag_distribution(duration_by_tag)
@@ -78,16 +80,28 @@ module Timet
78
80
  # @param duration_by_tag [Hash<String, Integer>] A hash where keys are tags and values are durations in seconds.
79
81
  # @return [void] This method outputs the formatted tag distribution to the console.
80
82
  def format_tag_distribution(duration_by_tag)
81
- block = '▅'
82
83
  total = duration_by_tag.values.sum
83
84
  return unless total.positive?
84
85
 
85
86
  factor = duration_by_tag.size < 3 ? 2 : 1
86
87
  sorted_duration_by_tag = duration_by_tag.sort_by { |_, duration| -duration }
88
+ process_and_print_tags(sorted_duration_by_tag, factor, total)
89
+ end
87
90
 
91
+ # Processes and prints the tag distribution information.
92
+ #
93
+ # @param sorted_duration_by_tag [Array<Array(String, Numeric)>] An array of arrays where each inner array contains a
94
+ # tag and its corresponding duration, sorted by duration in descending order.
95
+ # @param factor [Numeric] The factor used to adjust the bar length.
96
+ # @param total [Numeric] The total duration of all tags combined.
97
+ # @return [void] This method outputs the tag distribution information to the standard output.
98
+ def process_and_print_tags(sorted_duration_by_tag, factor, total)
99
+ block = '▅'
88
100
  sorted_duration_by_tag.each do |tag, duration|
89
101
  value = (duration.to_f / total * 100).round(2)
90
- puts "#{tag.rjust(8)}: #{value.to_s.rjust(7)}% \u001b[38;5;#{rand(256)}m#{block * (value / factor).to_i}\u001b[0m"
102
+ bar_length = (value / factor).to_i
103
+ color = rand(256)
104
+ puts "#{tag.rjust(8)}: #{value.to_s.rjust(7)}% \u001b[38;5;#{color}m#{block * bar_length}\u001b[0m"
91
105
  end
92
106
  end
93
107
 
@@ -105,14 +119,14 @@ module Timet
105
119
  # time_block = { "00" => 100, "01" => 200, ..., "23" => 300 }
106
120
  # print_time_block_chart(time_block)
107
121
  # # Output:
108
- # # ⏳ ↦ 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
109
- # # ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █
122
+ # # ⏳ ↦ [ 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ]
123
+ # # [ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █
110
124
  # #
111
125
  # # (followed by two newlines)
112
126
  #
113
127
  def print_time_block_chart(time_block)
114
128
  print_header
115
- print ' '
129
+ print ' [ '
116
130
  print_blocks(time_block)
117
131
  end
118
132
 
@@ -124,12 +138,13 @@ module Timet
124
138
  # @example
125
139
  # print_header
126
140
  # # Output:
127
- # # ⏳ ↦ 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
141
+ # # ⏳ ↦ [ 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
128
142
  #
129
143
  def print_header
130
144
  puts
131
- print '⏳ ↦ '
145
+ print '⏳ ↦ [ '
132
146
  (0..23).each { |hour| print format('%02d', hour).ljust(4) }
147
+ print ']'
133
148
  puts
134
149
  end
135
150
 
@@ -157,6 +172,7 @@ module Timet
157
172
  block_char = get_block_char(time_block[format('%02d', hour)])
158
173
  print (block_char * 2).ljust(4)
159
174
  end
175
+ print ']'
160
176
  puts "\n\n"
161
177
  end
162
178
 
@@ -7,7 +7,8 @@ module Timet
7
7
  #
8
8
  # @param result [Array] The result set containing time tracking items.
9
9
  #
10
- # @return [Symbol] The status of the time tracking result. Possible values are :no_items, :in_progress, or :complete.
10
+ # @return [Symbol] The status of the time tracking result. Possible values are
11
+ # :no_items, :in_progress, or :complete.
11
12
  #
12
13
  # @example Determine the status of an empty result set
13
14
  # StatusHelper.determine_status([]) # => :no_items
@@ -24,11 +24,13 @@ module Timet
24
24
  # Initializes a new instance of the TimeReport class.
25
25
  #
26
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'.
27
+ # @param filter [String, nil] The filter to apply when fetching items. Possible values include 'today',
28
+ # 'yesterday', 'week', 'month', or a date range in the format 'YYYY-MM-DD..YYYY-MM-DD'.
28
29
  # @param tag [String, nil] The tag to filter the items by.
29
30
  # @param csv [String, nil] The filename to use when exporting the report to CSV.
30
31
  #
31
- # @return [void] This method does not return a value; it performs side effects such as initializing the instance variables.
32
+ # @return [void] This method does not return a value; it performs side effects such as initializing the
33
+ # instance variables.
32
34
  #
33
35
  # @example Initialize a new TimeReport instance with a filter and tag
34
36
  # TimeReport.new(db, 'today', 'work', 'report.csv')
@@ -178,7 +180,7 @@ module Timet
178
180
  total = @items.map do |item|
179
181
  TimeHelper.calculate_duration(item[1], item[2])
180
182
  end.sum
181
- puts "|#{' ' * 43}\033[94mTotal: | #{@db.seconds_to_hms(total).rjust(8)} |\033[0m |"
183
+ puts "|#{' ' * 37}\033[94mTotal: | #{@db.seconds_to_hms(total).rjust(8)} |\033[0m |"
182
184
  puts format_table_separator
183
185
  end
184
186
 
data/lib/timet/version.rb CHANGED
@@ -6,6 +6,6 @@ module Timet
6
6
  # @return [String] The version number in the format 'major.minor.patch'.
7
7
  #
8
8
  # @example Get the version of the Timet application
9
- # Timet::VERSION # => '1.2.0'
10
- VERSION = '1.2.0'
9
+ # Timet::VERSION # => '1.2.1'
10
+ VERSION = '1.2.1'
11
11
  end
data/lib/timet.rb CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  # Require the Timet::Application class from the 'timet/application' file.
4
4
  #
5
- # @note This statement loads the Timet::Application class, which is responsible for handling the command-line interface and user commands.
5
+ # @note This statement loads the Timet::Application class, which is responsible for handling the command-line
6
+ # interface and user commands.
6
7
  require_relative 'timet/application'
7
8
 
8
9
  # Require the Timet::Database class from the 'timet/database' file.
@@ -12,5 +13,6 @@ require_relative 'timet/database'
12
13
 
13
14
  # Require the Timet::TimeReport class from the 'timet/time_report' file.
14
15
  #
15
- # @note This statement loads the Timet::TimeReport class, which is responsible for displaying a report of tracked time entries.
16
+ # @note This statement loads the Timet::TimeReport class, which is responsible for displaying a report
17
+ # of tracked time entries.
16
18
  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: 1.2.0
4
+ version: 1.2.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-11 00:00:00.000000000 Z
11
+ date: 2024-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor