timet 1.1.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 +4 -4
- data/CHANGELOG.md +69 -0
- data/README.md +37 -8
- data/lib/timet/application.rb +49 -25
- data/lib/timet/application_helper.rb +37 -10
- data/lib/timet/database.rb +20 -10
- data/lib/timet/formatter.rb +139 -8
- data/lib/timet/status_helper.rb +2 -1
- data/lib/timet/time_helper.rb +66 -0
- data/lib/timet/time_report.rb +16 -3
- data/lib/timet/version.rb +2 -2
- data/lib/timet.rb +4 -2
- data/timet.webp +0 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db3c3e635b08a071bc271be5788a4b689f0a28203dea81c2dc73a78c312b089e
|
4
|
+
data.tar.gz: 2c5c831731c0ec619064653b8239381a53e5c973f92f347e98a74de8c07547e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b98ea65edc254804e58c16ce0f200209ee57b9b3629fc182ec822c1b359380015bdaf281db64a9bd96acaa5ded45489f4464602ae90ab3e11916556e879e88a
|
7
|
+
data.tar.gz: fba083a2484a17a3af3efb5c0d2dcad4b08f463d162b6689c4a6445948d6f2bd517ef0b3efd794d0b0b05a818799798446a65902cf97ebe94519f884aa2c9927
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,74 @@
|
|
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
|
+
|
19
|
+
## [1.2.0] - 2024-10-11
|
20
|
+
|
21
|
+
**Improvements:**
|
22
|
+
|
23
|
+
- Enhanced the README to provide more detailed and user-friendly information about the tool.
|
24
|
+
- Added a visually appealing title and logo to make the README more engaging.
|
25
|
+
- Organized key features into bullet points for better readability.
|
26
|
+
- Provided clear and concise installation instructions.
|
27
|
+
- Made the command reference more user-friendly by adding a table of contents.
|
28
|
+
- Added more details about data storage and development guidelines.
|
29
|
+
- Encouraged contributions and provided guidelines for contributing.
|
30
|
+
- Made the support section more prominent.
|
31
|
+
- Ensured the license and code of conduct are clearly stated.
|
32
|
+
- Updated the `timet` gem version to 1.2.0 in `Gemfile.lock`.
|
33
|
+
- Refactored the `play_sound_and_notify` condition to use the `positive?` method for better readability.
|
34
|
+
- Enhanced table header formatting with a blinking effect for better user interaction.
|
35
|
+
- Added new methods for tag distribution and time block chart visualization:
|
36
|
+
- `format_tag_distribution`: Displays tag distribution with progress bars.
|
37
|
+
- `print_time_block_chart`: Prints the entire time block chart.
|
38
|
+
- `print_header`: Prints the header of the time block chart.
|
39
|
+
- `print_blocks`: Prints the block characters for each hour.
|
40
|
+
- `get_block_char`: Determines the block character based on value.
|
41
|
+
- Added `count_seconds_per_hour_block` method to count seconds per hour block.
|
42
|
+
- Added `aggregate_hash_values` method to aggregate hash values.
|
43
|
+
- Integrated new methods into `time_report` to enhance time tracking visualization.
|
44
|
+
- Updated the version number in `lib/timet/version.rb` to 1.2.0.
|
45
|
+
|
46
|
+
**Additional Considerations:**
|
47
|
+
|
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.
|
49
|
+
- Reviewers are encouraged to test the new visualization methods and provide feedback on their effectiveness and usability.
|
50
|
+
- The README updates should make it easier for new users to understand and use the `timet` tool.
|
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
|
+
|
3
72
|
## [1.0.0] - 2024-10-07
|
4
73
|
|
5
74
|
**Improvements:**
|
data/README.md
CHANGED
@@ -5,18 +5,42 @@
|
|
5
5
|
|
6
6
|
# Timet
|
7
7
|
|
8
|
+

|
9
|
+
|
8
10
|
Timet refers to a command-line tool designed to track your activities by recording the time spent on each task, allowing you to monitor your work hours and productivity directly from your terminal without needing a graphical interface; essentially, it's a way to log your time spent on different projects or tasks using simple text commands
|
9
11
|
|
10
|
-
|
12
|
+
**Key Features:**
|
11
13
|
|
12
|
-
|
14
|
+
- **Local Data Storage:** Timet utilizes SQLite to store your time tracking data locally, ensuring privacy and security.
|
15
|
+
- **Lightweight and Fast:** Its efficient design and local data storage make Timet a speedy and responsive tool.
|
16
|
+
- **Structured Data:** SQLite ensures your data is organized and easily accessible.
|
17
|
+
- **Scalability:** Timet can handle growing time tracking needs.
|
18
|
+
- **Data Integrity:** SQLite maintains the accuracy and consistency of your data.
|
19
|
+
- **Querying and Reporting:** Generate detailed reports for specific periods.
|
20
|
+
- **CSV Export:** Easily export your time tracking data to CSV format for further analysis or sharing.
|
21
|
+
- **Pomodoro Integration:** The pomodoro option in the start command enhances time tracking by integrating the Pomodoro Technique.
|
22
|
+
- **Block Time Plot:** Visualizes the distribution of tracked time across a 24-hour period, with bars in each column representing the amount of time tracked during that specific hour.
|
23
|
+
- **Tag Distribution Plot:** Illustrates the proportion of total tracked time allocated to each tag, showing the relative contribution of each tag to the overall time tracked.
|
13
24
|
|
14
|
-
|
15
|
-
- Scalability
|
16
|
-
- Data Integrity
|
17
|
-
- Querying and Reporting
|
25
|
+
Example:
|
18
26
|
|
19
|
-
|
27
|
+
```bash
|
28
|
+
Tracked time report [today]:
|
29
|
+
+-------+------------+--------+----------+----------+----------+--------------------------+
|
30
|
+
| Id | Date | Tag | Start | End | Duration | Notes |
|
31
|
+
+-------+------------+--------+----------+----------+----------+--------------------------+
|
32
|
+
| 20 | 2024-10-10 | Tag8 | 19:26:58 | 20:26:58 | 01:00:00 | Notes 2 |
|
33
|
+
| 19 | | Tag3 | 07:52:26 | 08:52:26 | 01:00:00 | Notes 7 |
|
34
|
+
+-------+------------+--------+----------+----------+----------+--------------------------+
|
35
|
+
| Total: | 02:00:00 | |
|
36
|
+
+-------+------------+--------+----------+----------+----------+--------------------------+
|
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
|
+
[ ▂▂ ▇▇ ▅▅ ▄▄ ]
|
40
|
+
|
41
|
+
Tag8: 50.0% ▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅
|
42
|
+
Tag3: 50.0% ▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅
|
43
|
+
```
|
20
44
|
|
21
45
|
## Requirements
|
22
46
|
|
@@ -207,9 +231,14 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/frankv
|
|
207
231
|
|
208
232
|
Many people have contacted me asking how to contribute. Any contribution, from a virtual coffee to a kind word, is greatly appreciated and helps me continue my work. Please only donate if you're able, as there are no refunds. Your support is entirely voluntary, and I thank you for your consideration.
|
209
233
|
|
234
|
+
**Bitcoin Address:**
|
235
|
+
```sh
|
236
|
+
bc1qkg9me2jsuhpzu2hp9kkpxagwtf9ewnyfl4kszl
|
237
|
+
```
|
238
|
+
|
210
239
|

|
211
240
|
|
212
|
-
|
241
|
+
---
|
213
242
|
|
214
243
|
## License
|
215
244
|
|
data/lib/timet/application.rb
CHANGED
@@ -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
|
49
|
-
#
|
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,
|
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)
|
@@ -66,7 +69,7 @@ module Timet
|
|
66
69
|
|
67
70
|
if VALID_STATUSES_FOR_INSERTION.include?(@db.last_item_status)
|
68
71
|
@db.insert_item(start_time, tag, notes)
|
69
|
-
play_sound_and_notify(pomodoro * 60, tag) if pomodoro
|
72
|
+
play_sound_and_notify(pomodoro * 60, tag) if pomodoro.positive?
|
70
73
|
end
|
71
74
|
summary
|
72
75
|
end
|
@@ -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
|
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
|
84
|
-
#
|
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
|
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,
|
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
|
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',
|
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
|
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
|
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
|
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'.
|
163
|
-
#
|
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
|
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
|
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
|
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
|
205
|
-
#
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
elsif
|
96
|
-
|
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
|
data/lib/timet/database.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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)
|
data/lib/timet/formatter.rb
CHANGED
@@ -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
|
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
|
@@ -14,9 +15,9 @@ module Timet
|
|
14
15
|
# @note The method constructs a string representing the table header and prints it.
|
15
16
|
def format_table_header
|
16
17
|
header = <<~TABLE
|
17
|
-
Tracked time report \u001b[31m[#{@filter}]\033[0m:
|
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
|
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,140 @@ 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
|
-
|
63
|
+
spaces = 17
|
64
|
+
return ' ' * spaces if notes.nil?
|
63
65
|
|
64
|
-
notes = "#{notes.slice(0,
|
65
|
-
notes.ljust(
|
66
|
+
notes = "#{notes.slice(0, spaces - 3)}..." if notes.length > spaces - 3
|
67
|
+
notes.ljust(spaces)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @!method format_tag_distribution(duration_by_tag)
|
71
|
+
# Formats and displays the tag distribution.
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# duration_by_tag = { "timet" => 3600, "nextjs" => 1800 }
|
75
|
+
# Formatter.format_tag_distribution(duration_by_tag)
|
76
|
+
# # Output:
|
77
|
+
# # timet: 66.67% \u001b[38;5;42m====================\u001b[0m
|
78
|
+
# # nextjs: 33.33% \u001b[38;5;42m==========\u001b[0m
|
79
|
+
#
|
80
|
+
# @param duration_by_tag [Hash<String, Integer>] A hash where keys are tags and values are durations in seconds.
|
81
|
+
# @return [void] This method outputs the formatted tag distribution to the console.
|
82
|
+
def format_tag_distribution(duration_by_tag)
|
83
|
+
total = duration_by_tag.values.sum
|
84
|
+
return unless total.positive?
|
85
|
+
|
86
|
+
factor = duration_by_tag.size < 3 ? 2 : 1
|
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
|
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 = '▅'
|
100
|
+
sorted_duration_by_tag.each do |tag, duration|
|
101
|
+
value = (duration.to_f / total * 100).round(2)
|
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"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Prints the entire time block chart.
|
109
|
+
#
|
110
|
+
# This method orchestrates the printing of the entire time block chart by calling
|
111
|
+
# the `print_header` and `print_blocks` methods. It also prints the separator line
|
112
|
+
# between the header and the blocks, and adds a double newline at the end for
|
113
|
+
# separation.
|
114
|
+
#
|
115
|
+
# @param time_block [Hash] A hash where the keys are formatted hour strings
|
116
|
+
# (e.g., "00", "01") and the values are the corresponding
|
117
|
+
# values to determine the block character.
|
118
|
+
# @example
|
119
|
+
# time_block = { "00" => 100, "01" => 200, ..., "23" => 300 }
|
120
|
+
# print_time_block_chart(time_block)
|
121
|
+
# # Output:
|
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
|
+
# # [ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █
|
124
|
+
# #
|
125
|
+
# # (followed by two newlines)
|
126
|
+
#
|
127
|
+
def print_time_block_chart(time_block)
|
128
|
+
print_header
|
129
|
+
print ' [ '
|
130
|
+
print_blocks(time_block)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Prints the header of the time block chart.
|
134
|
+
#
|
135
|
+
# This method outputs the header line of the chart, which includes the hours
|
136
|
+
# from 00 to 23, formatted and aligned for readability.
|
137
|
+
#
|
138
|
+
# @example
|
139
|
+
# print_header
|
140
|
+
# # Output:
|
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
|
142
|
+
#
|
143
|
+
def print_header
|
144
|
+
puts
|
145
|
+
print '⏳ ↦ [ '
|
146
|
+
(0..23).each { |hour| print format('%02d', hour).ljust(4) }
|
147
|
+
print ']'
|
148
|
+
puts
|
149
|
+
end
|
150
|
+
|
151
|
+
# Prints the block characters for each hour in the time block chart.
|
152
|
+
#
|
153
|
+
# This method iterates over each hour from 0 to 23, retrieves the corresponding
|
154
|
+
# block character using the `get_block_char` method, and prints it aligned for
|
155
|
+
# readability. It also adds a double newline at the end for separation.
|
156
|
+
#
|
157
|
+
# @param time_block [Hash] A hash where the keys are formatted hour strings
|
158
|
+
# (e.g., "00", "01") and the values are the corresponding
|
159
|
+
# values to determine the block character.
|
160
|
+
# @example
|
161
|
+
# time_block = { "00" => 100, "01" => 200, ..., "23" => 300 }
|
162
|
+
# print_blocks(time_block)
|
163
|
+
# # Output:
|
164
|
+
# # ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █
|
165
|
+
# #
|
166
|
+
# # (followed by two newlines)
|
167
|
+
#
|
168
|
+
def print_blocks(time_block)
|
169
|
+
return unless time_block
|
170
|
+
|
171
|
+
(0..23).each do |hour|
|
172
|
+
block_char = get_block_char(time_block[format('%02d', hour)])
|
173
|
+
print (block_char * 2).ljust(4)
|
174
|
+
end
|
175
|
+
print ']'
|
176
|
+
puts "\n\n"
|
177
|
+
end
|
178
|
+
|
179
|
+
# Determines the block character based on the value.
|
180
|
+
#
|
181
|
+
# @param value [Integer] The value to determine the block character for.
|
182
|
+
# @return [String] The block character corresponding to the value.
|
183
|
+
def get_block_char(value)
|
184
|
+
range_to_char = {
|
185
|
+
0..120 => ' ',
|
186
|
+
121..450 => '▁',
|
187
|
+
451..900 => '▂',
|
188
|
+
901..1350 => '▃',
|
189
|
+
1351..1800 => '▄',
|
190
|
+
1801..2250 => '▅',
|
191
|
+
2251..2700 => '▆',
|
192
|
+
2701..3150 => '▇',
|
193
|
+
3151..3600 => '█'
|
194
|
+
}
|
195
|
+
|
196
|
+
range_to_char.find { |range, _| range.include?(value) }&.last || ' '
|
66
197
|
end
|
67
198
|
end
|
68
199
|
end
|
data/lib/timet/status_helper.rb
CHANGED
@@ -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
|
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
|
data/lib/timet/time_helper.rb
CHANGED
@@ -168,5 +168,71 @@ module Timet
|
|
168
168
|
def self.current_timestamp
|
169
169
|
Time.now.utc.to_i
|
170
170
|
end
|
171
|
+
|
172
|
+
# Counts the number of seconds for each hour block between the given start and end times.
|
173
|
+
#
|
174
|
+
# This method calculates the number of seconds each event spans within each hour block
|
175
|
+
# and aggregates the results in a hash where the keys are the hour blocks (in 'HH' format)
|
176
|
+
# and the values are the total number of seconds for each hour block.
|
177
|
+
#
|
178
|
+
# @param start_time [Integer] The start time in seconds since the Unix epoch.
|
179
|
+
# @param end_time [Integer] The end time in seconds since the Unix epoch. If not provided,
|
180
|
+
# the current time will be used.
|
181
|
+
# @return [Hash] A hash where the keys are the hour blocks (in 'HH' format) and the values
|
182
|
+
# are the total number of seconds for each hour block.
|
183
|
+
# @example
|
184
|
+
# start_time = 1728577349 # 8:30 AM
|
185
|
+
# end_time = 1728579200 # 11:20 AM
|
186
|
+
# result = count_seconds_per_hour_block(start_time, end_time)
|
187
|
+
# # Output: {"08"=>1800, "09"=>1800, "10"=>3600, "11"=>1200}
|
188
|
+
#
|
189
|
+
def self.count_seconds_per_hour_block(start_time, end_time)
|
190
|
+
hour_blocks = Hash.new(0)
|
191
|
+
|
192
|
+
current_time = Time.at(start_time)
|
193
|
+
end_time = Time.at(end_time || current_timestamp)
|
194
|
+
|
195
|
+
while current_time < end_time
|
196
|
+
current_hour = current_time.hour
|
197
|
+
next_hour_boundary = Time.new(current_time.year, current_time.month, current_time.day, current_hour + 1)
|
198
|
+
|
199
|
+
block_end_time = [next_hour_boundary, end_time].min
|
200
|
+
seconds_in_block = (block_end_time - current_time).to_i
|
201
|
+
|
202
|
+
hour_block = current_time.strftime('%H')
|
203
|
+
hour_blocks[hour_block] += seconds_in_block
|
204
|
+
|
205
|
+
current_time = block_end_time
|
206
|
+
end
|
207
|
+
|
208
|
+
hour_blocks
|
209
|
+
end
|
210
|
+
|
211
|
+
# Aggregates the values of the same keys from an array of hashes.
|
212
|
+
#
|
213
|
+
# This method takes an array of hashes, reverses it, and then aggregates the values
|
214
|
+
# for the same keys into a single hash. If a key appears in multiple hashes, its
|
215
|
+
# values are summed.
|
216
|
+
#
|
217
|
+
# @param time_block [Array<Hash>] An array of hashes where each hash contains key-value pairs.
|
218
|
+
# @return [Hash] A hash where the keys are the aggregated keys from the input hashes
|
219
|
+
# and the values are the summed values for each key.
|
220
|
+
# @example
|
221
|
+
# time_block = [
|
222
|
+
# {"01": 10},
|
223
|
+
# {"01": 30},
|
224
|
+
# {"02": 50}
|
225
|
+
# ]
|
226
|
+
# result = aggregate_hash_values(time_block)
|
227
|
+
# # Output: {"01"=>40, "02"=>50}
|
228
|
+
#
|
229
|
+
def self.aggregate_hash_values(time_block)
|
230
|
+
time_block.reverse.each_with_object({}) do |hash, acc|
|
231
|
+
hash.each do |key, value|
|
232
|
+
acc[key] ||= 0
|
233
|
+
acc[key] += value
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
171
237
|
end
|
172
238
|
end
|
data/lib/timet/time_report.rb
CHANGED
@@ -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',
|
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
|
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')
|
@@ -51,12 +53,23 @@ module Timet
|
|
51
53
|
return puts 'No tracked time found for the specified filter.' if items.empty?
|
52
54
|
|
53
55
|
format_table_header
|
56
|
+
duration_by_tag = Hash.new(0)
|
57
|
+
time_block = []
|
54
58
|
items.each_with_index do |item, idx|
|
55
59
|
date = TimeHelper.extract_date(items, idx)
|
56
60
|
display_time_entry(item, date)
|
61
|
+
time_block << TimeHelper.count_seconds_per_hour_block(item[1], item[2])
|
62
|
+
duration_by_tag[item[3]] += TimeHelper.calculate_duration(item[1], item[2])
|
57
63
|
end
|
58
64
|
puts format_table_separator
|
59
65
|
total
|
66
|
+
|
67
|
+
if Time.now.to_i - items.map { |x| x[1] }.min < 86_400
|
68
|
+
time_block_reverse = TimeHelper.aggregate_hash_values(time_block)
|
69
|
+
print_time_block_chart(time_block_reverse)
|
70
|
+
end
|
71
|
+
|
72
|
+
format_tag_distribution(duration_by_tag)
|
60
73
|
end
|
61
74
|
|
62
75
|
# Displays a single row of the report.
|
@@ -167,7 +180,7 @@ module Timet
|
|
167
180
|
total = @items.map do |item|
|
168
181
|
TimeHelper.calculate_duration(item[1], item[2])
|
169
182
|
end.sum
|
170
|
-
puts "|#{' ' *
|
183
|
+
puts "|#{' ' * 37}\033[94mTotal: | #{@db.seconds_to_hms(total).rjust(8)} |\033[0m |"
|
171
184
|
puts format_table_separator
|
172
185
|
end
|
173
186
|
|
data/lib/timet/version.rb
CHANGED
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
|
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
|
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'
|
data/timet.webp
ADDED
Binary file
|
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.1
|
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
|
+
date: 2024-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -89,6 +89,7 @@ files:
|
|
89
89
|
- lib/timet/validation_edit_helper.rb
|
90
90
|
- lib/timet/version.rb
|
91
91
|
- sig/timet.rbs
|
92
|
+
- timet.webp
|
92
93
|
homepage: https://frankvielma.github.io/posts/timet-a-powerful-command-line-tool-for-tracking-your-time/
|
93
94
|
licenses:
|
94
95
|
- MIT
|