timet 0.9.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74f9106fb9a60ccd2e2446a58350d3de263459df41ffdf1aa57ca62d267da985
4
- data.tar.gz: 77de65d76048301e01e0fb1607862ead7e69ead1647546974ac48b027222b73b
3
+ metadata.gz: 7b90eeedf6f1653e70f0d90fc2181ebeec88c3788bbc88dbcd072f5a6f2fe821
4
+ data.tar.gz: c2fbc67fd45bb9d3923a91f737f03fc2a74599193cbe932fbeaadbb44a2d21be
5
5
  SHA512:
6
- metadata.gz: 409da2fabebd3071832c3c67498015a358cf973e9a41da970969aee97e53e779c8372e9c72c3dad835020644cf163be0138097337ca5589343f3f2364d2d177f
7
- data.tar.gz: 537cd9eeba10e3bd43246d3c92f736b91ce171568437e15a22b0a0460171185cc5482f3b45348ef759b4daebb016d59126205f2192a168e618f0445b4384b88b
6
+ metadata.gz: 475f77e1febf29ccdb1e17bfdd309a829b3d309d4d670afe95ede0926ab897173006ab89d165de4f87c3c3663c5114a08e54ada2f6835453fc4d99fb86134c62
7
+ data.tar.gz: ca865940ccafb9e60eb682d9085202e3946e1029f5df01b0ece8e998d9dcbe3746f1a196d4638e9a94cb587425198cba4fd93182e2934185d4e849407d34f81c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.9.1] - 2024-10-04
4
+
5
+ **Improvements:**
6
+
7
+ - Added YARD documentation
8
+ - Refactored the `start` method to use `@db.insert_item` directly if the last item status is valid for insertion.
9
+ - Removed the `insert_item_if_valid` private method as it is no longer needed.
10
+ - Updated the README.md to reflect the latest changes and improvements.
11
+ - Added a badge displaying the current gem version in the README.md.
12
+
3
13
  ## [0.9.0] - 2024-10-03
4
14
 
5
15
  **Improvements:**
data/README.md CHANGED
@@ -1,10 +1,11 @@
1
+ [![Gem Version](https://badge.fury.io/rb/timet.svg)](https://badge.fury.io/rb/timet)
1
2
  ![timet workflow](https://github.com/frankvielma/timet/actions/workflows/ci.yml/badge.svg)
2
3
  [![Maintainability](https://api.codeclimate.com/v1/badges/44d57b6c561b9be717f5/maintainability)](https://codeclimate.com/github/frankvielma/timet/maintainability)
3
4
  [![Test Coverage](https://api.codeclimate.com/v1/badges/44d57b6c561b9be717f5/test_coverage)](https://codeclimate.com/github/frankvielma/timet/test_coverage)
4
5
 
5
6
  # Timet
6
7
 
7
- Timet is a command line time tracking with reports. It's simple to track your hours for work with Timet, whether you're curious about how you allocate your time.
8
+ 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
8
9
 
9
10
  Timet utilizes SQLite to store your time tracking data. This means your data is stored locally and securely, with no need for external databases or cloud storage. This makes Timet lightweight, fast, and perfect for users who value privacy and control over their data.
10
11
 
@@ -154,7 +155,6 @@ gem install timet
154
155
  | `timet su [start_date]..[end_date]` | Display a report of tracked time for a date range. | `timet su 2024-01-02..2024-01-03` |
155
156
 
156
157
 
157
-
158
158
  ### Date Range in Summary
159
159
 
160
160
  The `timet summary` command now supports specifying a date range for generating reports. This allows users to filter and summarize data within specific date intervals. The date format is in ISO 8601 format (YYYY-MM-DD).
@@ -19,6 +19,7 @@ module Timet
19
19
  class Application < Thor
20
20
  include ValidationEditHelper
21
21
  include ApplicationHelper
22
+ include TimeHelper
22
23
 
23
24
  def initialize(*args)
24
25
  super
@@ -36,15 +37,40 @@ module Timet
36
37
 
37
38
  desc "start [tag] --notes=''", "start time tracking --notes='my notes...'"
38
39
  option :notes, type: :string, desc: 'Add a note'
40
+ # Starts a new tracking session with the given tag and optional notes.
41
+ #
42
+ # @param tag [String] The tag associated with the tracking session. This is a required parameter.
43
+ # @param notes [String, nil] Optional notes to be associated with the tracking session. If not provided, it defaults to the value in `options[:notes]`.
44
+ #
45
+ # @return [void] This method does not return a value; it performs side effects such as inserting a tracking item and generating a summary.
46
+ #
47
+ # @example Start a tracking session with a tag and notes
48
+ # start('work', 'Starting work on project X')
49
+ #
50
+ # @example Start a tracking session with only a tag
51
+ # start('break')
52
+ #
53
+ # @note The method uses `TimeHelper.current_timestamp` to get the current timestamp for the start time.
54
+ # @note The method calls `summary` to generate a summary after inserting the tracking item.
39
55
  def start(tag, notes = nil)
40
56
  start_time = TimeHelper.current_timestamp
41
57
  notes = options[:notes] || notes
42
58
 
43
- insert_item_if_valid(start_time, tag, notes)
59
+ @db.insert_item(start_time, tag, notes) if VALID_STATUSES_FOR_INSERTION.include?(@db.last_item_status)
44
60
  summary
45
61
  end
46
62
 
47
63
  desc 'stop', 'stop time tracking'
64
+ # Stops the current tracking session if there is one in progress.
65
+ #
66
+ # @return [void] This method does not return a value; it performs side effects such as updating the tracking item and generating a summary.
67
+ #
68
+ # @example Stop the current tracking session
69
+ # stop
70
+ #
71
+ # @note The method checks if the last tracking item is in progress by calling `@db.last_item_status`.
72
+ # @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.
73
+ # @note The method then fetches the last item using `@db.last_item` and generates a summary if the result is not nil.
48
74
  def stop
49
75
  if @db.last_item_status == :in_progress
50
76
  last_id = @db.fetch_last_id
@@ -58,6 +84,16 @@ module Timet
58
84
  end
59
85
 
60
86
  desc 'resume (r)', 'resume last task'
87
+ # Resumes the last tracking session if it was completed.
88
+ #
89
+ # @return [void] This method does not return a value; it performs side effects such as resuming a tracking session or providing feedback.
90
+ #
91
+ # @example Resume the last tracking session
92
+ # resume
93
+ #
94
+ # @note The method checks the status of the last tracking item using `@db.last_item_status`.
95
+ # @note If the last item is in progress, it prints a message indicating that a task is currently being tracked.
96
+ # @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.
61
97
  def resume
62
98
  status = @db.last_item_status
63
99
 
@@ -77,6 +113,26 @@ module Timet
77
113
  desc 'summary (su) [filter] [tag] --csv=csv_filename',
78
114
  ' [filter] => [today (t), yesterday (y), week (w), month (m), [start_date]..[end_date]] [tag]'
79
115
  option :csv, type: :string, desc: 'Export to CSV file'
116
+ # Generates a summary of tracking items based on the provided filter and tag, and optionally exports the summary to a CSV file.
117
+ #
118
+ # @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]'.
119
+ # @param tag [String, nil] The tag to filter the tracking items by.
120
+ #
121
+ # @return [void] This method does not return a value; it performs side effects such as displaying the summary and exporting to CSV if specified.
122
+ #
123
+ # @example Generate a summary for today
124
+ # summary('today')
125
+ #
126
+ # @example Generate a summary for a specific tag
127
+ # summary(nil, 'work')
128
+ #
129
+ # @example Generate a summary for a date range and export to CSV
130
+ # summary('2023-01-01..2023-01-31', nil, csv: 'summary.csv')
131
+ #
132
+ # @note The method initializes a `TimeReport` object with the database, filter, tag, and optional CSV filename.
133
+ # @note The method calls `display` on the `TimeReport` object to show the summary.
134
+ # @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.
135
+ # @note If no items are found to export, it prints a message indicating that no items were found.
80
136
  def summary(filter = nil, tag = nil)
81
137
  csv_filename = options[:csv]&.split('.')&.first
82
138
  summary = TimeReport.new(@db, filter, tag, csv_filename)
@@ -92,6 +148,25 @@ module Timet
92
148
 
93
149
  desc 'edit (e) [id] [field] [value]',
94
150
  'edit a task, [field] (notes, tag, start or end) and [value] are optional parameters'
151
+ # Edits a specific tracking item by its ID, allowing the user to modify fields such as notes, tag, start time, or end time.
152
+ #
153
+ # @param id [Integer] The ID of the tracking item to be edited.
154
+ # @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.
155
+ # @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.
156
+ #
157
+ # @return [void] This method does not return a value; it performs side effects such as updating the tracking item and displaying the updated item.
158
+ #
159
+ # @example Edit the notes of a tracking item with ID 1
160
+ # edit(1, 'notes', 'Updated notes')
161
+ #
162
+ # @example Edit a tracking item with ID 2, prompting for the field and new value
163
+ # edit(2)
164
+ #
165
+ # @note The method first attempts to find the tracking item by its ID using `@db.find_item(id)`.
166
+ # @note If the item is found, it displays the current item details using `display_item(item)`.
167
+ # @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.
168
+ # @note The method then validates and updates the item using `validate_and_update(item, field, new_value)`.
169
+ # @note Finally, it displays the updated item details using `display_item(updated_item)`.
95
170
  def edit(id, field = nil, new_value = nil)
96
171
  item = @db.find_item(id)
97
172
  return puts "No tracked time found for id: #{id}" unless item
@@ -107,6 +182,19 @@ module Timet
107
182
  end
108
183
 
109
184
  desc 'delete (d) [id]', 'delete a task'
185
+ # Deletes a specific tracking item by its ID after confirming with the user.
186
+ #
187
+ # @param id [Integer] The ID of the tracking item to be deleted.
188
+ #
189
+ # @return [void] This method does not return a value; it performs side effects such as deleting the tracking item and displaying a confirmation message.
190
+ #
191
+ # @example Delete a tracking item with ID 1
192
+ # delete(1)
193
+ #
194
+ # @note The method first attempts to find the tracking item by its ID using `@db.find_item(id)`.
195
+ # @note If the item is found, it displays the item details using `TimeReport.new(@db).show_row(item)`.
196
+ # @note The method then prompts the user for confirmation using `TTY::Prompt.new.yes?('Are you sure you want to delete this entry?')`.
197
+ # @note If the user confirms, the method deletes the item and prints a confirmation message using `delete_item_and_print_message(id, "Deleted #{id}")`.
110
198
  def delete(id)
111
199
  item = @db.find_item(id)
112
200
  return puts "No tracked time found for id: #{id}" unless item
@@ -118,6 +206,17 @@ module Timet
118
206
  end
119
207
 
120
208
  desc 'cancel (c)', 'cancel active time tracking'
209
+ # Cancels the active time tracking session by deleting the last tracking item.
210
+ #
211
+ # @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.
212
+ #
213
+ # @example Cancel the active time tracking session
214
+ # cancel
215
+ #
216
+ # @note The method fetches the ID of the last tracking item using `@db.fetch_last_id`.
217
+ # @note It checks if the last item is in progress by comparing `@db.last_item_status` with `:complete`.
218
+ # @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}")`.
219
+ # @note If there is no active time tracking, it prints a message indicating that there is no active time tracking.
121
220
  def cancel
122
221
  id = @db.fetch_last_id
123
222
  return puts 'There is no active time tracking' if @db.last_item_status == :complete
@@ -125,18 +224,33 @@ module Timet
125
224
  delete_item_and_print_message(id, "Canceled active time tracking #{id}")
126
225
  end
127
226
 
227
+ # Determines whether the application should exit when a command fails.
228
+ #
229
+ # @return [Boolean] Returns `true`, indicating that the application should exit when a command fails.
230
+ #
231
+ # @example Check if the application should exit on failure
232
+ # MyClass.exit_on_failure? # => true
233
+ #
234
+ # @note This method is typically used in command-line applications to control the behavior when a command fails.
235
+ # @note Returning `true` means that the application will exit immediately if a command fails, which is useful for ensuring that errors are handled gracefully.
128
236
  def self.exit_on_failure?
129
237
  true
130
238
  end
131
239
 
132
240
  private
133
241
 
134
- def insert_item_if_valid(start_time, tag, notes)
135
- return unless VALID_STATUSES_FOR_INSERTION.include?(@db.last_item_status)
136
-
137
- @db.insert_item(start_time, tag, notes)
138
- end
139
-
242
+ # Deletes a tracking item from the database by its ID and prints a confirmation message.
243
+ #
244
+ # @param id [Integer] The ID of the tracking item to be deleted.
245
+ # @param message [String] The message to be printed after the item is deleted.
246
+ #
247
+ # @return [void] This method does not return a value; it performs side effects such as deleting the tracking item and printing a message.
248
+ #
249
+ # @example Delete a tracking item with ID 1 and print a confirmation message
250
+ # delete_item_and_print_message(1, 'Deleted item 1')
251
+ #
252
+ # @note The method deletes the tracking item from the database using `@db.delete_item(id)`.
253
+ # @note After deleting the item, the method prints the provided message using `puts message`.
140
254
  def delete_item_and_print_message(id, message)
141
255
  @db.delete_item(id)
142
256
  puts message
@@ -3,21 +3,64 @@
3
3
  module Timet
4
4
  # Provides helper methods for the Timet application.
5
5
  module ApplicationHelper
6
+ # Displays the details of a tracking item.
7
+ #
8
+ # @param item [Hash] The tracking item to be displayed.
9
+ #
10
+ # @return [void] This method does not return a value; it performs side effects such as displaying the item details.
11
+ #
12
+ # @example Display the details of a tracking item
13
+ # display_item(item)
14
+ #
15
+ # @note The method initializes a `TimeReport` object with the database and calls `show_row` to display the item details.
6
16
  def display_item(item)
7
17
  TimeReport.new(@db).show_row(item)
8
18
  end
9
19
 
20
+ # Prompts the user to enter a new value for a specific field of a tracking item.
21
+ #
22
+ # @param item [Hash] The tracking item to be edited.
23
+ # @param field [String] The field to be updated.
24
+ #
25
+ # @return [String] The new value entered by the user.
26
+ #
27
+ # @example Prompt for a new value for the 'notes' field
28
+ # prompt_for_new_value(item, 'notes')
29
+ #
30
+ # @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.
10
32
  def prompt_for_new_value(item, field)
11
33
  current_value = field_value(item, field)
12
34
  prompt = TTY::Prompt.new(active_color: :green)
13
35
  prompt.ask("Update #{field} (#{current_value}):")
14
36
  end
15
37
 
38
+ # Prompts the user to select a field to edit from a list of available fields.
39
+ #
40
+ # @return [String] The selected field in lowercase.
41
+ #
42
+ # @example Prompt for a field to edit
43
+ # select_field_to_edit
44
+ #
45
+ # @note The method uses `TTY::Prompt.new` to display a list of available fields for the user to select from.
46
+ # @note The method returns the selected field in lowercase.
16
47
  def select_field_to_edit
17
48
  prompt = TTY::Prompt.new(active_color: :green)
18
49
  prompt.select('Edit Field?', Timet::Application::FIELD_INDEX.keys.map(&:capitalize), active_color: :cyan).downcase
19
50
  end
20
51
 
52
+ # Retrieves the value of a specific field from a tracking item.
53
+ #
54
+ # @param item [Hash] The tracking item.
55
+ # @param field [String] The field to retrieve the value for.
56
+ #
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.
58
+ #
59
+ # @example Retrieve the value of the 'notes' field
60
+ # field_value(item, 'notes')
61
+ #
62
+ # @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`.
21
64
  def field_value(item, field)
22
65
  index = Timet::Application::FIELD_INDEX[field]
23
66
  value = item[index]
@@ -1,19 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'sqlite3'
4
-
4
+ require_relative 'status_helper'
5
5
  module Timet
6
6
  # Provides database access for managing time tracking data.
7
7
  class Database
8
+ include StatusHelper
9
+
10
+ # The default path to the SQLite database file.
8
11
  DEFAULT_DATABASE_PATH = File.join(Dir.home, '.timet.db')
9
12
 
13
+ # Initializes a new instance of the Database class.
14
+ #
15
+ # @param database_path [String] The path to the SQLite database file. Defaults to DEFAULT_DATABASE_PATH.
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.
18
+ #
19
+ # @example Initialize a new Database instance with the default path
20
+ # Database.new
21
+ #
22
+ # @example Initialize a new Database instance with a custom path
23
+ # Database.new('/path/to/custom.db')
24
+ #
25
+ # @note The method creates a new SQLite3 database connection and initializes the necessary tables if they do not already exist.
10
26
  def initialize(database_path = DEFAULT_DATABASE_PATH)
11
27
  @db = SQLite3::Database.new(database_path)
12
28
  create_table
13
29
  add_notes
14
30
  end
15
31
 
16
- # Creates the items table if it doesn't already exist
32
+ # Creates the items table if it doesn't already exist.
33
+ #
34
+ # @return [void] This method does not return a value; it performs side effects such as executing SQL to create the table.
35
+ #
36
+ # @example Create the items table
37
+ # create_table
38
+ #
39
+ # @note The method executes SQL to create the 'items' table with columns for id, start, end, and tag.
17
40
  def create_table
18
41
  execute_sql(<<-SQL)
19
42
  CREATE TABLE IF NOT EXISTS items (
@@ -26,6 +49,13 @@ module Timet
26
49
  end
27
50
 
28
51
  # Adds a new column named "notes" to the "items" table if it doesn't exist.
52
+ #
53
+ # @return [void] This method does not return a value; it performs side effects such as executing SQL to add the column.
54
+ #
55
+ # @example Add the notes column to the items table
56
+ # add_notes
57
+ #
58
+ # @note The method checks if the 'notes' column already exists and adds it if it does not.
29
59
  def add_notes
30
60
  table_name = 'items'
31
61
  new_column_name = 'notes'
@@ -37,45 +67,129 @@ module Timet
37
67
  puts "Column '#{new_column_name}' added to table '#{table_name}'."
38
68
  end
39
69
 
40
- # Inserts a new item into the items table
70
+ # Inserts a new item into the items table.
71
+ #
72
+ # @param start [Integer] The start time of the item.
73
+ # @param tag [String] The tag associated with the item.
74
+ # @param notes [String] The notes associated with the item.
75
+ #
76
+ # @return [void] This method does not return a value; it performs side effects such as executing SQL to insert the item.
77
+ #
78
+ # @example Insert a new item into the items table
79
+ # insert_item(1633072800, 'work', 'Completed task X')
80
+ #
81
+ # @note The method executes SQL to insert a new row into the 'items' table.
41
82
  def insert_item(start, tag, notes)
42
83
  execute_sql('INSERT INTO items (start, tag, notes) VALUES (?, ?, ?)', [start, tag, notes])
43
84
  end
44
85
 
86
+ # Updates an existing item in the items table.
87
+ #
88
+ # @param id [Integer] The ID of the item to be updated.
89
+ # @param field [String] The field to be updated.
90
+ # @param value [String, Integer, nil] The new value for the specified field.
91
+ #
92
+ # @return [void] This method does not return a value; it performs side effects such as executing SQL to update the item.
93
+ #
94
+ # @example Update the tag of an item with ID 1
95
+ # update_item(1, 'tag', 'updated_work')
96
+ #
97
+ # @note The method executes SQL to update the specified field of the item with the given ID.
45
98
  def update_item(id, field, value)
46
99
  return if %w[start end].include?(field) && value.nil?
47
100
 
48
101
  execute_sql("UPDATE items SET #{field}='#{value}' WHERE id = #{id}")
49
102
  end
50
103
 
104
+ # Deletes an item from the items table.
105
+ #
106
+ # @param id [Integer] The ID of the item to be deleted.
107
+ #
108
+ # @return [void] This method does not return a value; it performs side effects such as executing SQL to delete the item.
109
+ #
110
+ # @example Delete an item with ID 1
111
+ # delete_item(1)
112
+ #
113
+ # @note The method executes SQL to delete the item with the given ID from the 'items' table.
51
114
  def delete_item(id)
52
115
  execute_sql("DELETE FROM items WHERE id = #{id}")
53
116
  end
54
117
 
55
- # Fetches the ID of the last inserted item
118
+ # Fetches the ID of the last inserted item.
119
+ #
120
+ # @return [Integer, nil] The ID of the last inserted item, or nil if no items exist.
121
+ #
122
+ # @example Fetch the last inserted item ID
123
+ # fetch_last_id
124
+ #
125
+ # @note The method executes SQL to fetch the ID of the last inserted item.
56
126
  def fetch_last_id
57
127
  result = execute_sql('SELECT id FROM items ORDER BY id DESC LIMIT 1').first
58
128
  result ? result[0] : nil
59
129
  end
60
130
 
131
+ # Fetches the last item from the items table.
132
+ #
133
+ # @return [Array, nil] The last item as an array, or nil if no items exist.
134
+ #
135
+ # @example Fetch the last item
136
+ # last_item
137
+ #
138
+ # @note The method executes SQL to fetch the last item from the 'items' table.
61
139
  def last_item
62
140
  execute_sql('SELECT * FROM items ORDER BY id DESC LIMIT 1').first
63
141
  end
64
142
 
143
+ # Determines the status of the last item in the items table.
144
+ #
145
+ # @return [Symbol] The status of the last item. Possible values are :no_items, :in_progress, or :complete.
146
+ #
147
+ # @example Determine the status of the last item
148
+ # last_item_status
149
+ #
150
+ # @note The method executes SQL to fetch the last item and determines its status using the `StatusHelper` module.
65
151
  def last_item_status
66
152
  result = execute_sql('SELECT id, end FROM items ORDER BY id DESC LIMIT 1')
67
153
  StatusHelper.determine_status(result)
68
154
  end
69
155
 
156
+ # Finds an item in the items table by its ID.
157
+ #
158
+ # @param id [Integer] The ID of the item to be found.
159
+ #
160
+ # @return [Array, nil] The item as an array, or nil if the item does not exist.
161
+ #
162
+ # @example Find an item with ID 1
163
+ # find_item(1)
164
+ #
165
+ # @note The method executes SQL to find the item with the given ID in the 'items' table.
70
166
  def find_item(id)
71
167
  execute_sql("select * from items where id=#{id}").first
72
168
  end
73
169
 
170
+ # Fetches all items from the items table that have a start time greater than or equal to today.
171
+ #
172
+ # @return [Array] An array of items.
173
+ #
174
+ # @example Fetch all items from today
175
+ # all_items
176
+ #
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.
74
178
  def all_items
75
179
  execute_sql("SELECT * FROM items where start >= '#{Date.today.to_time.to_i}' ORDER BY id DESC")
76
180
  end
77
181
 
78
- # Executes a SQL query and returns the result
182
+ # Executes a SQL query and returns the result.
183
+ #
184
+ # @param sql [String] The SQL query to execute.
185
+ # @param params [Array] The parameters to bind to the SQL query.
186
+ #
187
+ # @return [Array] The result of the SQL query.
188
+ #
189
+ # @example Execute a SQL query
190
+ # execute_sql('SELECT * FROM items WHERE id = ?', [1])
191
+ #
192
+ # @note The method executes the given SQL query with the provided parameters and returns the result.
79
193
  def execute_sql(sql, params = [])
80
194
  @db.execute(sql, params)
81
195
  rescue SQLite3::SQLException => e
@@ -83,12 +197,28 @@ module Timet
83
197
  []
84
198
  end
85
199
 
86
- # Closes the database connection
200
+ # Closes the database connection.
201
+ #
202
+ # @return [void] This method does not return a value; it performs side effects such as closing the database connection.
203
+ #
204
+ # @example Close the database connection
205
+ # close
206
+ #
207
+ # @note The method closes the SQLite3 database connection.
87
208
  def close
88
209
  @db&.close
89
210
  end
90
211
 
91
212
  # Converts a given number of seconds into a human-readable HH:MM:SS format.
213
+ #
214
+ # @param seconds [Integer] The number of seconds to convert.
215
+ #
216
+ # @return [String] The formatted time in HH:MM:SS format.
217
+ #
218
+ # @example Convert 3661 seconds to HH:MM:SS format
219
+ # seconds_to_hms(3661) # => '01:01:01'
220
+ #
221
+ # @note The method converts the given number of seconds into hours, minutes, and seconds, and formats them as HH:MM:SS.
92
222
  def seconds_to_hms(seconds)
93
223
  hours, remainder = seconds.divmod(3600)
94
224
  minutes, seconds = remainder.divmod(60)
@@ -4,6 +4,14 @@ module Timet
4
4
  # This module is responsible for formatting the output of the `timet` application.
5
5
  # It provides methods for formatting the table header, separators, and rows.
6
6
  module Formatter
7
+ # Formats the header of the time tracking report table.
8
+ #
9
+ # @return [void] This method does not return a value; it performs side effects such as printing the formatted header.
10
+ #
11
+ # @example Format and print the table header
12
+ # format_table_header
13
+ #
14
+ # @note The method constructs a string representing the table header and prints it.
7
15
  def format_table_header
8
16
  header = <<~TABLE
9
17
  Tracked time report \u001b[31m[#{@filter}]\033[0m:
@@ -14,16 +22,42 @@ module Timet
14
22
  puts header
15
23
  end
16
24
 
25
+ # Formats the separator line for the time tracking report table.
26
+ #
27
+ # @return [String] The formatted separator line.
28
+ #
29
+ # @example Get the formatted table separator
30
+ # format_table_separator # => '+-------+------------+--------+----------+----------+----------+--------------------------+'
31
+ #
32
+ # @note The method returns a string representing the separator line for the table.
17
33
  def format_table_separator
18
34
  '+-------+------------+--------+----------+----------+----------+--------------------------+'
19
35
  end
20
36
 
37
+ # Formats a row of the time tracking report table.
38
+ #
39
+ # @param row [Array] The row data to be formatted.
40
+ # @return [String] The formatted row.
41
+ #
42
+ # @example Format a table row
43
+ # format_table_row(1, 'work', '2023-10-01', '12:00:00', '14:00:00', 7200, 'Completed task X')
44
+ #
45
+ # @note The method formats each element of the row and constructs a string representing the formatted row.
21
46
  def format_table_row(*row)
22
47
  id, tag, start_date, start_time, end_time, duration, notes = row
23
48
  "| #{id.to_s.rjust(5)} | #{start_date} | #{tag.ljust(6)} | #{start_time.split[1]} | " \
24
49
  "#{end_time.split[1].rjust(8)} | #{@db.seconds_to_hms(duration).rjust(8)} | #{format_notes(notes)} |"
25
50
  end
26
51
 
52
+ # Formats the notes column of the time tracking report table.
53
+ #
54
+ # @param notes [String, nil] The notes to be formatted.
55
+ # @return [String] The formatted notes.
56
+ #
57
+ # @example Format notes
58
+ # format_notes('This is a long note that needs to be truncated')
59
+ #
60
+ # @note The method truncates the notes to a maximum of 20 characters and pads them to a fixed width.
27
61
  def format_notes(notes)
28
62
  return ' ' * 23 if notes.nil?
29
63
 
@@ -1,8 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Timet
4
- # Determines the status of a time tracking result based on the presence and end time of items.
4
+ # Provides helper methods to determine the status of time tracking results.
5
5
  module StatusHelper
6
+ # Determines the status of a time tracking result based on the presence and end time of items.
7
+ #
8
+ # @param result [Array] The result set containing time tracking items.
9
+ #
10
+ # @return [Symbol] The status of the time tracking result. Possible values are :no_items, :in_progress, or :complete.
11
+ #
12
+ # @example Determine the status of an empty result set
13
+ # StatusHelper.determine_status([]) # => :no_items
14
+ #
15
+ # @example Determine the status of a result set with an in-progress item
16
+ # StatusHelper.determine_status([[1, nil]]) # => :in_progress
17
+ #
18
+ # @example Determine the status of a result set with a completed item
19
+ # StatusHelper.determine_status([[1, 1633072800]]) # => :complete
20
+ #
21
+ # @note The method checks if the result set is empty and returns :no_items if true.
22
+ # @note If the last item in the result set has no end time, it returns :in_progress.
23
+ # @note If the last item in the result set has an end time, it returns :complete.
6
24
  def self.determine_status(result)
7
25
  return :no_items if result.empty?
8
26
 
@@ -7,38 +7,91 @@ module Timet
7
7
  # - calculating the duration between two timestamps
8
8
  # - converting a Date object to a timestamp
9
9
  module TimeHelper
10
+ # Formats a timestamp into a specific format.
11
+ #
12
+ # @param timestamp [Integer] The timestamp to format.
13
+ # @return [String, nil] The formatted time string in 'YYYY-MM-DD HH:MM:SS' format, or nil if the timestamp is nil.
14
+ #
15
+ # @example Format a timestamp
16
+ # TimeHelper.format_time(1633072800) # => '2021-10-01 12:00:00'
10
17
  def self.format_time(timestamp)
11
18
  return nil if timestamp.nil?
12
19
 
13
20
  Time.at(timestamp).strftime('%Y-%m-%d %H:%M:%S')
14
21
  end
15
22
 
23
+ # Converts a timestamp to a date string.
24
+ #
25
+ # @param timestamp [Integer] The timestamp to convert.
26
+ # @return [String, nil] The date string in 'YYYY-MM-DD' format, or nil if the timestamp is nil.
27
+ #
28
+ # @example Convert a timestamp to a date string
29
+ # TimeHelper.timestamp_to_date(1633072800) # => '2021-10-01'
16
30
  def self.timestamp_to_date(timestamp)
17
31
  return nil if timestamp.nil?
18
32
 
19
33
  Time.at(timestamp).strftime('%Y-%m-%d')
20
34
  end
21
35
 
36
+ # Converts a timestamp to a time string.
37
+ #
38
+ # @param timestamp [Integer] The timestamp to convert.
39
+ # @return [String, nil] The time string in 'HH:MM:SS' format, or nil if the timestamp is nil.
40
+ #
41
+ # @example Convert a timestamp to a time string
42
+ # TimeHelper.timestamp_to_time(1633072800) # => '12:00:00'
22
43
  def self.timestamp_to_time(timestamp)
23
44
  return nil if timestamp.nil?
24
45
 
25
46
  Time.at(timestamp).strftime('%H:%M:%S')
26
47
  end
27
48
 
49
+ # Calculates the duration between two timestamps.
50
+ #
51
+ # @param start_time [Integer] The start timestamp.
52
+ # @param end_time [Integer, nil] The end timestamp. If nil, the current timestamp is used.
53
+ # @return [Integer] The duration in seconds.
54
+ #
55
+ # @example Calculate the duration between two timestamps
56
+ # TimeHelper.calculate_duration(1633072800, 1633076400) # => 3600
28
57
  def self.calculate_duration(start_time, end_time)
29
58
  end_time = end_time ? Time.at(end_time) : current_timestamp
30
59
  (end_time - start_time).to_i
31
60
  end
32
61
 
62
+ # Converts a Date object to a timestamp.
63
+ #
64
+ # @param date [Date] The Date object to convert.
65
+ # @return [Integer] The timestamp.
66
+ #
67
+ # @example Convert a Date object to a timestamp
68
+ # TimeHelper.date_to_timestamp(Date.new(2021, 10, 1)) # => 1633072800
33
69
  def self.date_to_timestamp(date)
34
70
  date.to_time.to_i
35
71
  end
36
72
 
73
+ # Calculates the end time based on the start date and end date.
74
+ #
75
+ # @param start_date [Date] The start date.
76
+ # @param end_date [Date, nil] The end date. If nil, the start date + 1 day is used.
77
+ # @return [Integer] The end timestamp.
78
+ #
79
+ # @example Calculate the end time
80
+ # TimeHelper.calculate_end_time(Date.new(2021, 10, 1), Date.new(2021, 10, 2)) # => 1633159200
37
81
  def self.calculate_end_time(start_date, end_date)
38
82
  end_date = end_date ? end_date + 1 : start_date + 1
39
83
  date_to_timestamp(end_date)
40
84
  end
41
85
 
86
+ # Extracts the date from a list of items based on the index.
87
+ #
88
+ # @param items [Array] The list of items.
89
+ # @param idx [Integer] The index of the current item.
90
+ # @return [String, nil] The date string in 'YYYY-MM-DD' format, or nil if the date is the same as the previous item.
91
+ #
92
+ # @example Extract the date from a list of items
93
+ # items = [[1, 1633072800], [2, 1633159200]]
94
+ # TimeHelper.extract_date(items, 1) # => '2021-10-02'
42
95
  def self.extract_date(items, idx)
43
96
  current_start_date = items[idx][1]
44
97
  date = TimeHelper.timestamp_to_date(current_start_date)
@@ -50,17 +103,17 @@ module Timet
50
103
  # Formats a time string into a standard HH:MM:SS format.
51
104
  #
52
105
  # @param input [String] The input string to format.
53
- # @return [String] The formatted time string in HH:MM:SS format, or nil if the input is invalid.
54
- #
55
- # @example
56
- # TimeHelper.format_time_string('123456') # => "12:34:56"
57
- # TimeHelper.format_time_string('1234567') # => "12:34:56"
58
- # TimeHelper.format_time_string('1234') # => "12:34:00"
59
- # TimeHelper.format_time_string('123') # => "12:30:00"
60
- # TimeHelper.format_time_string('12') # => "12:00:00"
61
- # TimeHelper.format_time_string('1') # => "01:00:00"
62
- # TimeHelper.format_time_string('127122') # => nil
63
- # TimeHelper.format_time_string('abc') # => nil
106
+ # @return [String, nil] The formatted time string in HH:MM:SS format, or nil if the input is invalid.
107
+ #
108
+ # @example Format a time string
109
+ # TimeHelper.format_time_string('123456') # => "12:34:56"
110
+ # TimeHelper.format_time_string('1234567') # => "12:34:56"
111
+ # TimeHelper.format_time_string('1234') # => "12:34:00"
112
+ # TimeHelper.format_time_string('123') # => "12:30:00"
113
+ # TimeHelper.format_time_string('12') # => "12:00:00"
114
+ # TimeHelper.format_time_string('1') # => "01:00:00"
115
+ # TimeHelper.format_time_string('127122') # => nil
116
+ # TimeHelper.format_time_string('abc') # => nil
64
117
  def self.format_time_string(input)
65
118
  return nil if input.nil? || input.empty?
66
119
 
@@ -73,6 +126,13 @@ module Timet
73
126
  format('%<hours>02d:%<minutes>02d:%<seconds>02d', hours: hours, minutes: minutes, seconds: seconds)
74
127
  end
75
128
 
129
+ # Parses time components from a string of digits.
130
+ #
131
+ # @param digits [String] The string of digits to parse.
132
+ # @return [Array] An array containing the hours, minutes, and seconds.
133
+ #
134
+ # @example Parse time components
135
+ # TimeHelper.parse_time_components('123456') # => [12, 34, 56]
76
136
  def self.parse_time_components(digits)
77
137
  padded_digits = case digits.size
78
138
  when 1 then "0#{digits}0000"
@@ -85,10 +145,26 @@ module Timet
85
145
  padded_digits.scan(/.{2}/).map(&:to_i)
86
146
  end
87
147
 
148
+ # Validates the time components.
149
+ #
150
+ # @param hours [Integer] The hours component.
151
+ # @param minutes [Integer] The minutes component.
152
+ # @param seconds [Integer] The seconds component.
153
+ # @return [Boolean] True if the time components are valid, otherwise false.
154
+ #
155
+ # @example Validate time components
156
+ # TimeHelper.valid_time?(12, 34, 56) # => true
157
+ # TimeHelper.valid_time?(25, 34, 56) # => false
88
158
  def self.valid_time?(hours, minutes, seconds)
89
159
  hours < 24 && minutes < 60 && seconds < 60
90
160
  end
91
161
 
162
+ # Returns the current timestamp.
163
+ #
164
+ # @return [Integer] The current timestamp.
165
+ #
166
+ # @example Get the current timestamp
167
+ # TimeHelper.current_timestamp
92
168
  def self.current_timestamp
93
169
  Time.now.utc.to_i
94
170
  end
@@ -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,6 +171,17 @@ 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]
@@ -101,6 +195,14 @@ module Timet
101
195
  end
102
196
  end
103
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'.
104
206
  def date_ranges
105
207
  today = Date.today
106
208
  {
@@ -111,6 +213,18 @@ module Timet
111
213
  }
112
214
  end
113
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.
114
228
  def filter_by_date_range(start_date, end_date = nil, tag = nil)
115
229
  start_time = TimeHelper.date_to_timestamp(start_date)
116
230
  end_time = TimeHelper.calculate_end_time(start_date, end_date)
@@ -120,6 +234,16 @@ module Timet
120
234
  )
121
235
  end
122
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.
123
247
  def formatted_filter(filter)
124
248
  filter_map = {
125
249
  'today' => %w[today t],
@@ -137,6 +261,16 @@ module Timet
137
261
  'today'
138
262
  end
139
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.
140
274
  def valid_date_format?(date_string)
141
275
  date_format_single = /^\d{4}-\d{2}-\d{2}$/
142
276
  date_format_range = /^\d{4}-\d{2}-\d{2}\.\.\d{4}-\d{2}-\d{2}$/
@@ -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
 
@@ -24,6 +39,18 @@ module Timet
24
39
 
25
40
  private
26
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.
27
54
  def process_and_update_time_field(item, field, date_value, id)
28
55
  formatted_date = TimeHelper.format_time_string(date_value)
29
56
 
@@ -39,10 +66,28 @@ module Timet
39
66
  end
40
67
  end
41
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')
42
77
  def print_error(message)
43
78
  puts "\u001b[31mInvalid date: #{message}\033[0m"
44
79
  end
45
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')
46
91
  def update_time_field(item, field, formatted_value)
47
92
  field_index = Timet::Application::FIELD_INDEX[field]
48
93
  timestamp = item[field_index]
@@ -51,6 +96,17 @@ module Timet
51
96
  DateTime.strptime(current_time.join(' '), '%Y-%m-%d %H:%M:%S %z').to_time
52
97
  end
53
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)
54
110
  def valid_time_value?(item, field, new_value_epoch, id)
55
111
  item_start = fetch_item_start(item)
56
112
  item_end = fetch_item_end(item)
@@ -64,18 +120,51 @@ module Timet
64
120
  end
65
121
  end
66
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)
67
131
  def fetch_item_start(item)
68
132
  item[Timet::Application::FIELD_INDEX['start']]
69
133
  end
70
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)
71
143
  def fetch_item_end(item)
72
144
  item[Timet::Application::FIELD_INDEX['end']] || TimeHelper.current_timestamp
73
145
  end
74
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)
75
156
  def fetch_item_before_end(id, item_start)
76
157
  @db.find_item(id - 1)&.dig(Timet::Application::FIELD_INDEX['end']) || item_start
77
158
  end
78
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)
79
168
  def fetch_item_after_start(id)
80
169
  @db.find_item(id + 1)&.dig(Timet::Application::FIELD_INDEX['start']) || TimeHelper.current_timestamp
81
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.9.0'
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.9.0
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-03 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