timet 1.6.1.1 → 1.6.2

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: 5827ed2755115ce5974bb35145f83b6d0de26d94e2e0fc60983117a55d4d8854
4
- data.tar.gz: de50d2db6b024c348b65bc00fb6020b1d92dcf5eeb78138dc2e2dc59cb117296
3
+ metadata.gz: 60cf8b709509db9c6a6a5bd27ed60db0c3c08a22f44aeced1d758d0790f74e5a
4
+ data.tar.gz: 7dd85ae0a812e4102218c553256740f184313008b694d8478eced986392ecc90
5
5
  SHA512:
6
- metadata.gz: 400c4b23c9260412c3afeb9ed62ebd51c545d5b689bf165e7c6c90abd01e8c665dbcbf6eb690ff030c2a42a9f83c8e2fe58258995c9130630fd9e3597a1082b3
7
- data.tar.gz: 6be9dc88ef4eca0ef785d6d5272195a1d6ad3b5c6f5a65bc44bd12d2d36c5d0fef7f11b8cabe741e92b83fe782433ffb74f01d4635c061e6b9e95753c593a3d1
6
+ metadata.gz: cfa8731849d0ac4f4e1b7d3da1d676e3b93a2e3c730d332b33a12bc8c589ef33214a36f5a37c4e6793ff07fdcf534b085a10b6e412c15d4052f2075c5b4fe02a
7
+ data.tar.gz: 226ebaef071767418b9ac6257d334e45e1aff0d65fa19968deb5eba198524f3145093b520e2c7e6c69e31ba5b222018aa819e6f03a5f63c5bc41a18d233a1d2b
data/.qlty/qlty.toml ADDED
File without changes
data/.rubocop.yml CHANGED
@@ -4,6 +4,7 @@ AllCops:
4
4
  SuggestExtensions: false
5
5
  Exclude:
6
6
  - timet.gemspec
7
+ - "vendor/**/*" # Exclude vendor/bundle gems
7
8
 
8
9
  Metrics/MethodLength:
9
10
  Max: 15
@@ -11,7 +12,7 @@ Metrics/MethodLength:
11
12
  plugins: rubocop-rspec
12
13
 
13
14
  RSpec/ExampleLength:
14
- Max: 15
15
+ Max: 15
15
16
 
16
17
  Metrics/CyclomaticComplexity:
17
18
  Max: 8
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## [1.6.2] - 2025-12-28
2
+
3
+ **Improvements:**
4
+
5
+ - **Database Hardening:** Added strict validations for table/column names in `add_column` and allowed fields in `update_item`. `execute_sql` now raises `SQLite3::SQLException` instead of suppressing it, improving error visibility.
6
+ - **Time Parsing Robustness:** Enhanced `TimeHelper#update_time_field` to robustly parse "HH:MM:SS" and "HH:MM" time strings using proper time construction.
7
+ - **Documentation Updates:** Updated README with explicit examples for `resume --pomodoro` and `summary --report` usage, and improved command reference formatting.
8
+ - **Refactoring:** Renamed `Timet::Utils.convert_to_datetime` to `convert_to_time` to better reflect that it returns a `Time` object, and updated related specs.
9
+ - **Dependency Updates:** Updated `aws-sdk-s3` to `~> 1.209.0`, `dotenv` to `~> 3.2`, `rubocop-rake` to `~> 0.7`, and other dependencies in `Gemfile` and `Gemfile.lock`.
10
+ - **CI/CD Enhancements:** Replaced DeepSource integration with Qlty for code analysis and test coverage reporting. Simplified CI workflow and added RuboCop with `continue-on-error` configuration.
11
+ - **Code Quality:** Applied RuboCop fixes including frozen string literals, numeric predicate corrections, and redundant interpolation fixes.
12
+
13
+ **Bug Fixes:**
14
+
15
+ - **Data Consistency:** Updated `update_time_columns` to ensure `updated_at` and `created_at` are populated with a fallback time (start time or current time) if the end time is nil.
16
+ - **Error Propagation:** Fixed `execute_sql` to raise exceptions instead of silently returning empty arrays, improving debugging capabilities.
17
+ - **Configuration:** Updated `.rubocop.yml` to exclude vendor directory and prevent interference from gem configurations.
18
+
1
19
  ## [1.6.1] - 2025-10-10
2
20
 
3
21
  **New Features:**
@@ -11,7 +29,6 @@
11
29
  - **README Updates:** Added new examples for the `--search` option and updated the project homepage URL.
12
30
  - **Time Report Filtering:** Enhanced `TimeReport` to incorporate the `--search` option for filtering items by tag or notes.
13
31
 
14
-
15
32
  ## [1.6.0] - 2025-09-24
16
33
 
17
34
  **New Features:**
@@ -28,7 +45,6 @@
28
45
  - **README Updates:** Added new interactive mode examples for the `edit` command and updated the project homepage URL.
29
46
  - **Timezone Consistency:** Improved time handling in `ValidationEditHelper` specs by consistently using `Time.parse().getlocal()` for better timezone accuracy.
30
47
 
31
-
32
48
  ## [1.5.9] - 2025-07-30
33
49
 
34
50
  **Improvements:**
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/timet.svg)](https://badge.fury.io/rb/timet)
2
2
  ![timet workflow](https://github.com/frankvielma/timet/actions/workflows/ci.yml/badge.svg)
3
- [![DeepSource](https://app.deepsource.com/gh/frankvielma/timet.svg/?label=active+issues&show_trend=true&token=RV8_VCNrXIfEU7NL9mk9MSuP)](https://app.deepsource.com/gh/frankvielma/timet/)
3
+ [![Maintainability](https://qlty.sh/gh/frankvielma/projects/timet/maintainability.svg)](https://qlty.sh/gh/frankvielma/projects/timet)
4
+
4
5
  # Timet
5
6
 
6
7
  ![Timet](timet.webp)
7
8
 
8
-
9
9
  ## Table of Contents
10
10
 
11
11
  - [🔑 Key Features](#key-features)
@@ -19,7 +19,6 @@
19
19
  - [Contributing](#contributing)
20
20
  - [License](#license)
21
21
 
22
-
23
22
  [Timet](https://rubygems.org/gems/timet) is a command-line tool designed to track your activities by recording the time spent on each task. This tool allows you to monitor your work hours and productivity directly from your terminal, eliminating the need for a graphical interface. Essentially, it's a way to log your time spent on different projects or tasks using simple text commands.
24
23
 
25
24
  <h2 id="key-features">🔑 Key Features:</h2>
@@ -45,6 +44,7 @@
45
44
  ![Timet demo](timet1.gif)
46
45
 
47
46
  <a name="requirements"></a>
47
+
48
48
  <h2 id="requirements">✔️ Requirements</h2>
49
49
 
50
50
  - Ruby version: >= 3.0.0
@@ -56,6 +56,7 @@ For older versions of Ruby and Sqlite:
56
56
  - [Ruby >= 2.4](https://github.com/frankvielma/timet/tree/ruby-2.4.0)
57
57
 
58
58
  <a name="installation"></a>
59
+
59
60
  ## 💾 Installation
60
61
 
61
62
  Install the gem by executing:
@@ -65,6 +66,7 @@ gem install timet
65
66
  ```
66
67
 
67
68
  <a name="usage"></a>
69
+
68
70
  ## ⏳ Usage
69
71
 
70
72
  ### Command Aliases
@@ -125,6 +127,7 @@ gem install timet
125
127
  Once configured, Timet will automatically send a notification to your Discord channel when a Pomodoro session ends.
126
128
 
127
129
  ---
130
+
128
131
  ---
129
132
 
130
133
  - **`timet stop`:** Stops tracking the current task, records the elapsed time, and displays the total time spent on all tasks.
@@ -152,6 +155,12 @@ Once configured, Timet will automatically send a notification to your Discord ch
152
155
  timet resume 1
153
156
  ```
154
157
 
158
+ To resume with a Pomodoro timer:
159
+
160
+ ```bash
161
+ timet resume 1 --pomodoro=25
162
+ ```
163
+
155
164
  ```
156
165
  Tracked time report [today]:
157
166
  +-------+------------+--------+----------+----------+----------+--------------------------+
@@ -216,27 +225,33 @@ Once configured, Timet will automatically send a notification to your Discord ch
216
225
  ```
217
226
 
218
227
  <a name="command-reference"></a>
228
+
219
229
  ## 📋 Command Reference
220
230
 
221
- | Command | Description | Example Usage |
222
- | -------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------- |
223
- | `timet start [tag] --notes='' --pomodoro=[time]` | Start tracking time for a task labeled [tag] and notes (optional). | `timet start Task "My notes" 25` |
224
- | `timet stop` | Stop tracking time. | `timet stop` |
225
- | `timet summary today (t)` | Display a report of tracked time for today. | `timet su t` or `timet su` |
226
- | `timet summary yesterday (y)` | Display a report of tracked time for yesterday. | `timet su y` |
227
- | `timet summary week (w)` | Display a report of tracked time for the week. | `timet su w` |
228
- | `timet summary month (m)` | Display a report of tracked time for the month. | `timet su m` |
229
- | `timet su t --csv=[filename]` | Display a report of tracked time for today and export to CSV file | `timet su t --csv=file.csv` |
230
- | `timet su w --ics=[filename]` | Display a report of tracked time for week and export to iCalendar file | `timet su w --ics=file.csv` |
231
- | `timet su t --report` | Display a detailed report of tag distribution for today. | `timet su t --report` |
232
- | `timet summary [time_scope] --search=[query]`| Display a report of tracked time filtered by tag or notes. | `timet su week --search="bug"` |
233
- | `timet delete [id]` | Delete a task by its ID. | `timet d [id]` |
234
- | `timet cancel` | Cancel active time tracking. | `timet c` |
235
- | `timet edit [id]` | Update a task's notes, tag, start, or end fields. | `timet e [id]` |
236
- | `timet su [date]` | Display a report of tracked time for a specific date. | `timet su 2024-01-03` |
237
- | `timet su [start_date]..[end_date]` | Display a report of tracked time for a date range. | `timet su 2024-01-02..2024-01-03` |
238
- | `timet resume (r) [id]` | Resume tracking a task by ID or the last completed task. | `timet resume [id]` |
239
- | `timet sync` | Sync local db with remote (S3) external db | `timet sync` |
231
+ | Command | Description | Example Usage |
232
+ | ------------------------------------------------ | ---------------------------------------------------------------------- | --------------------------------- |
233
+ | `timet start [tag] --notes='' --pomodoro=[time]` | Start tracking time for a task labeled [tag] and notes (optional). | `timet start Task "My notes" 25` |
234
+ | `timet stop` | Stop tracking time. | `timet stop` |
235
+ | `timet summary today (t)` | Display a report of tracked time for today. | `timet su t` or `timet su` |
236
+ | `timet summary yesterday (y)` | Display a report of tracked time for yesterday. | `timet su y` |
237
+ | `timet summary week (w)` | Display a report of tracked time for the week. | `timet su w` |
238
+ | `timet summary month (m)` | Display a report of tracked time for the month. | `timet su m` |
239
+ | `timet su t --csv=[filename]` | Display a report of tracked time for today and export to CSV file | `timet su t --csv=file.csv` |
240
+ | `timet su w --ics=[filename]` | Display a report of tracked time for week and export to iCalendar file | `timet su w --ics=file.csv` |
241
+ | `timet su --report` | Display a detailed report of tag distribution for today. | `timet su --report` |
242
+ | `timet su t --report` | Display a detailed report of tag distribution for today. | `timet su t --report` |
243
+ | `timet su w --report` | Display a detailed report of tag distribution for the week. | `timet su w --report` |
244
+ | `timet summary [time_scope] [tag]` | Display a report of tracked time filtered by tag. | `timet su week mytag` |
245
+ | `timet summary [time_scope] --search=[query]` | Display a report of tracked time filtered by tag or notes. | `timet su week --search="bug"` |
246
+ | `timet delete [id]` | Delete a task by its ID. | `timet d [id]` |
247
+ | `timet cancel` | Cancel active time tracking. | `timet c` |
248
+ | `timet edit [id]` | Update a task's notes, tag, start, or end fields. | `timet e [id]` |
249
+ | `timet su [date]` | Display a report of tracked time for a specific date. | `timet su 2024-01-03` |
250
+ | `timet su [start_date]..[end_date]` | Display a report of tracked time for a date range. | `timet su 2024-01-02..2024-01-03` |
251
+ | `timet resume (r) [id]` | Resume tracking a task by ID or the last completed task. | `timet resume [id]` |
252
+ | `timet resume (r) [id] --pomodoro=[minutes]` | Resume tracking with a Pomodoro timer. | `timet resume 1 --pomodoro=25` |
253
+ | `timet sync` | Sync local db with remote (S3) external db | `timet sync` |
254
+ | `timet version` | Display the current version. | `timet version` |
240
255
 
241
256
  ### Date Range in Summary
242
257
 
@@ -257,11 +272,13 @@ The `timet summary` command now supports specifying a date range for generating
257
272
  ```
258
273
 
259
274
  <a name="data"></a>
275
+
260
276
  ## 🗃️ Data
261
277
 
262
278
  Timet's data is stored in `~/.timet`.
263
279
 
264
280
  <a name="s3-cloud-backup-configuration"></a>
281
+
265
282
  ## 🔒 S3 Cloud Backup Configuration
266
283
 
267
284
  Timet supports backing up and syncing your time tracking data with S3-compatible storage services (such as Supabase S3). To configure S3 backup, follow these steps:
@@ -284,8 +301,6 @@ S3_REGION=your_s3_region
284
301
  - Regularly rotate your S3 access credentials
285
302
  - Implement appropriate IAM policies to restrict bucket access
286
303
 
287
-
288
-
289
304
  ## Development
290
305
 
291
306
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -73,7 +73,11 @@ module Timet
73
73
  # @note If the column does not exist, the method executes an SQL `ALTER TABLE` statement to add the column.
74
74
  # @note The method prints a message indicating that the column has been added.
75
75
  def add_column(table_name, new_column_name, date_type)
76
- result = execute_sql("SELECT count(*) FROM pragma_table_info('items') where name='#{new_column_name}'")
76
+ raise 'Invalid table name' unless table_name == 'items'
77
+ raise 'Invalid column name' unless /\A[a-zA-Z0-9_]+\z/.match?(new_column_name)
78
+ raise 'Invalid date type' unless %w[INTEGER TEXT BOOLEAN].include?(date_type)
79
+
80
+ result = execute_sql("SELECT count(*) FROM pragma_table_info('items') where name=?", [new_column_name])
77
81
  column_exists = result[0][0].positive?
78
82
  return if column_exists
79
83
 
@@ -115,6 +119,9 @@ module Timet
115
119
  #
116
120
  # @note The method executes SQL to update the specified field of the item with the given ID.
117
121
  def update_item(id, field, value)
122
+ allowed_fields = %w[tag notes start end deleted updated_at created_at]
123
+ raise "Invalid field: #{field}" unless allowed_fields.include?(field)
124
+
118
125
  execute_sql("UPDATE items SET #{field} = ?, updated_at = ? WHERE id = ?", [value, Time.now.utc.to_i, id])
119
126
  end
120
127
 
@@ -203,6 +210,7 @@ module Timet
203
210
  #
204
211
  # @param id [Integer, nil] The ID of the item to check. If nil, the last item in the table is used.
205
212
  #
213
+ # @see StatusHelper#determine_status
206
214
  def item_status(id = nil)
207
215
  id = fetch_last_id if id.nil?
208
216
  determine_status(find_item(id))
@@ -221,9 +229,6 @@ module Timet
221
229
  # @note The method executes the given SQL query with the provided parameters and returns the result.
222
230
  def execute_sql(sql, params = [])
223
231
  @db.execute(sql, params)
224
- rescue SQLite3::SQLException => e
225
- puts "Error: #{e.message}"
226
- []
227
232
  end
228
233
 
229
234
  # Closes the database connection.
@@ -315,8 +320,10 @@ module Timet
315
320
  result = execute_sql('SELECT * FROM items WHERE updated_at IS NULL OR created_at IS NULL')
316
321
  result.each do |item|
317
322
  id = item[0]
323
+ start_time = item[1]
318
324
  end_time = item[2]
319
- execute_sql('UPDATE items SET updated_at = ?, created_at = ? WHERE id = ?', [end_time, end_time, id])
325
+ fallback_time = end_time || start_time || Time.now.to_i
326
+ execute_sql('UPDATE items SET updated_at = ?, created_at = ? WHERE id = ?', [fallback_time, fallback_time, id])
320
327
  end
321
328
  end
322
329
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'net/http'
2
4
  require 'uri'
3
5
  require 'json'
@@ -83,7 +85,7 @@ module Timet
83
85
  # @return [void]
84
86
  def self.pomodoro_ended(duration)
85
87
  break_duration = (duration / 5).to_i # Assuming a 1/5th break duration
86
- break_duration = 5 if break_duration == 0 # Minimum 5 minute break
88
+ break_duration = 5 if break_duration.zero? # Minimum 5 minute break
87
89
  embed = {
88
90
  title: 'Pomodoro Session Ended! 🎉',
89
91
  description: "Time for a #{break_duration} minute break!",
@@ -75,7 +75,7 @@ module Timet
75
75
  # --- Individual Category Explanations ---
76
76
  explanations << 'Category Breakdown'.bold
77
77
  time_stats.sorted_duration_by_tag.each do |tag, duration|
78
- explanation = "#{"#{tag.capitalize}".bold}:"
78
+ explanation = "#{tag.capitalize.to_s.bold}:"
79
79
 
80
80
  # Percentage
81
81
  percentage = (duration.to_f / total * 100).round(1)
@@ -278,9 +278,24 @@ module Timet
278
278
  def self.update_time_field(item, field, new_time)
279
279
  field_index = Timet::Application::FIELD_INDEX[field]
280
280
  timestamp = item[field_index]
281
- edit_time = Time.at(timestamp || item[1]).to_s.split
282
- edit_time[1] = new_time
283
- DateTime.strptime(edit_time.join(' '), '%Y-%m-%d %H:%M:%S %z').to_time
281
+ base_time = Time.at(timestamp || item[1])
282
+
283
+ # Extract hours, minutes, and seconds from the new_time string
284
+ # Supports format "HH:MM:SS" or "HH:MM"
285
+ parts = new_time.split(':').map(&:to_i)
286
+ hours = parts[0]
287
+ minutes = parts[1]
288
+ seconds = parts[2] || 0
289
+
290
+ Time.new(
291
+ base_time.year,
292
+ base_time.month,
293
+ base_time.day,
294
+ hours,
295
+ minutes,
296
+ seconds,
297
+ base_time.utc_offset
298
+ )
284
299
  end
285
300
  end
286
301
  end
data/lib/timet/utils.rb CHANGED
@@ -23,12 +23,12 @@ module Timet
23
23
  end
24
24
  end
25
25
 
26
- # Converts a timestamp to a DateTime object.
26
+ # Converts a timestamp to a Time object.
27
27
  #
28
28
  # @param timestamp [Integer] the timestamp to convert
29
- # @return [DateTime] the converted DateTime object
30
- def self.convert_to_datetime(timestamp)
31
- Time.at(timestamp).to_datetime
29
+ # @return [Time] the converted Time object
30
+ def self.convert_to_time(timestamp)
31
+ Time.at(timestamp)
32
32
  end
33
33
 
34
34
  # Provides predefined date ranges for filtering.
@@ -82,8 +82,8 @@ module Timet
82
82
  #
83
83
  # @note The method validates the date format for single dates and date ranges.
84
84
  def self.valid_date_format?(date_string)
85
- date_format_single = /^\d{4}-\d{2}-\d{2}$/
86
- date_format_range = /^\d{4}-\d{2}-\d{2}\.\.\d{4}-\d{2}-\d{2}$/
85
+ date_format_single = /\A\d{4}-\d{2}-\d{2}\z/
86
+ date_format_range = /\A\d{4}-\d{2}-\d{2}\.\.\d{4}-\d{2}-\d{2}\z/
87
87
 
88
88
  date_string.match?(date_format_single) || date_string.match?(date_format_range)
89
89
  end
@@ -93,8 +93,8 @@ module Timet
93
93
  # @param event [Icalendar::Event] the event object
94
94
  # @param item [Array] the item containing event details
95
95
  def self.assign_event_attributes(event, item)
96
- dtstart = convert_to_datetime(item[1])
97
- dtend = convert_to_datetime(item[2] || TimeHelper.current_timestamp)
96
+ dtstart = convert_to_time(item[1])
97
+ dtend = convert_to_time(item[2] || TimeHelper.current_timestamp)
98
98
 
99
99
  event.dtstart = dtstart
100
100
  event.dtend = dtend
data/lib/timet/version.rb CHANGED
@@ -6,6 +6,6 @@ module Timet
6
6
  # @return [String] The version number in the format 'major.minor.patch'.
7
7
  #
8
8
  # @example Get the version of the Timet application
9
- # Timet::VERSION # => '1.6.1.1'
10
- VERSION = '1.6.1.1'
9
+ # Timet::VERSION # => '1.6.2'
10
+ VERSION = '1.6.2'
11
11
  end
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.6.1.1
4
+ version: 1.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frank Vielma
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-10 00:00:00.000000000 Z
11
+ date: 2025-12-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -106,14 +106,14 @@ dependencies:
106
106
  requirements:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: '1.178'
109
+ version: '1.209'
110
110
  type: :runtime
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - "~>"
115
115
  - !ruby/object:Gem::Version
116
- version: '1.178'
116
+ version: '1.209'
117
117
  description: Timet is a command-line time tracker that keeps track of your activities.
118
118
  email:
119
119
  - frankvielma@gmail.com
@@ -124,6 +124,7 @@ extensions: []
124
124
  extra_rdoc_files: []
125
125
  files:
126
126
  - ".deepsource.toml"
127
+ - ".qlty/qlty.toml"
127
128
  - ".reek.yml"
128
129
  - ".rspec"
129
130
  - ".rubocop.yml"