timet 1.5.6 → 1.5.7
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 +27 -0
- data/lib/timet/application.rb +20 -20
- data/lib/timet/block_char_helper.rb +33 -0
- data/lib/timet/item_data_helper.rb +59 -0
- data/lib/timet/table.rb +2 -1
- data/lib/timet/time_block_chart.rb +85 -111
- data/lib/timet/time_helper.rb +17 -0
- data/lib/timet/time_report.rb +9 -9
- data/lib/timet/time_report_helper.rb +1 -116
- data/lib/timet/time_update_helper.rb +93 -0
- data/lib/timet/time_validation_helper.rb +174 -0
- data/lib/timet/utils.rb +130 -0
- data/lib/timet/validation_edit_helper.rb +97 -121
- data/lib/timet/version.rb +2 -2
- data/lib/timet/week_info.rb +62 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 488c0136f2b11a62046a2f507b054d346a5def75dc3a32609ea51872bb411e6b
|
4
|
+
data.tar.gz: c28bbe32a4558ab6755e438ba7c5a53f879d6c036ae68706782b62d8ca26133c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1e74dfa8e0d6fdacf0fd8c458ade7667e2f2722376aea9307a5dab7b68b6a0a561b475432cf666564ba6664c18b6f80f94aed0bd88c3f6703ead36aa5d1cf84
|
7
|
+
data.tar.gz: 70608773c6f504e38637b7d4b2d71a852d7ed20c5679170c082d302748e1e3e6e4be2d1d4b909ce49e2ae45052dc3ac2cd02ba9a51106aba723211d3efa3c66a
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,30 @@
|
|
1
|
+
## [1.5.7] - 2025-05-16
|
2
|
+
|
3
|
+
**Improvements:**
|
4
|
+
|
5
|
+
- Extracted collision checks in [`ValidationEditHelper`](lib/timet/validation_edit_helper.rb) into dedicated private methods (`check_collision_with_previous_item`, `check_collision_with_next_item`).
|
6
|
+
- Enhanced time collision validation in [`ValidationEditHelper`](lib/timet/validation_edit_helper.rb): renamed `validate_collision` to `validate_time_collisions`, added a `field` argument for specific checks ('start'/'end'), implemented distinct collision logic, and updated `perform_validation` to use the new method for both fields.
|
7
|
+
- Improved time validation logic structure by extracting `determine_start_base_date_time` and `determine_end_base_date_time` from `determine_base_date_time`, and introducing `validate_time_order` to centralize start/end time order validation.
|
8
|
+
- Centralized datetime creation by introducing `create_datetime_from_components` in [`TimeHelper`](lib/timet/time_helper.rb).
|
9
|
+
- Improved time validation and determination logic in [`TimeValidationHelper`](lib/timet/time_validation_helper.rb) and [`ValidationEditHelper`](lib/timet/validation_edit_helper.rb): refactored `determine_base_date_time` to use a `case` statement, extracted `validate_time_difference` for 24-hour difference validation, and removed the unused `time_str` parameter from `determine_and_create_datetime`.
|
10
|
+
- Centralized time validation logic to the new [`TimeValidationHelper`](lib/timet/time_validation_helper.rb) module, moving relevant methods from [`ValidationEditHelper`](lib/timet/validation_edit_edit_helper.rb).
|
11
|
+
- Refactored time validation and item data helpers by moving time validation logic to [`TimeUpdateHelper`](lib/timet/time_update_helper.rb) and item data fetching methods to [`ItemDataHelper`](lib/timet/item_data_helper.rb).
|
12
|
+
- Extracted time parsing, base date determination, datetime creation, end time adjustment, and validation logic into dedicated private methods in [`lib/timet/validation_edit_helper.rb`](lib/timet/validation_edit_helper.rb).
|
13
|
+
- Improved time validation and editing logic in [`ValidationEditHelper`](lib/timet/validation_edit_helper.rb): anchored end time date to start time date, handled end time on the next day, enforced < 24 hour duration, and preserved original time on empty input.
|
14
|
+
- Centralized utility methods into a new [`Timet::Utils`](lib/timet/utils.rb) module.
|
15
|
+
- Improved [`WeekInfo`](lib/timet/week_info.rb) initialization to accept a `Date` object and display string, and refactored its responsibilities for clarity and Reek compliance.
|
16
|
+
- Improved [`TimeBlockChart`](lib/timet/time_block_chart.rb) readability by replacing magic numbers with constants and ensuring chronological date order in `print_blocks`.
|
17
|
+
- Refactored [`TimeBlockChart`](lib/timet/time_block_chart.rb) for clarity and Reek compliance, moving separator printing responsibility and the `CHAR_MAPPING` constant to appropriate locations/helpers.
|
18
|
+
- Refactored database initialization logic in [`Timet::Application`](lib/timet/application.rb) for clarity and testability, encapsulating `initialize_database` within a `no_commands` block.
|
19
|
+
- Updated gem dependencies, including `aws-sdk-s3`, `icalendar`, `rubocop`, and `rubocop-rspec`.
|
20
|
+
|
21
|
+
**Bug Fixes:**
|
22
|
+
|
23
|
+
- Corrected `perform_validation` in [`ValidationEditHelper`](lib/timet/validation_edit_helper.rb) to accept and utilize the `item` object for correct context in collision checks.
|
24
|
+
- Modified `create_new_datetime` to derive date components from the parsed time component, allowing users to specify a full date and time for 'start' or 'end' fields.
|
25
|
+
- Fixed a bug in [`WeekInfo#format_and_print_date_info`](lib/timet/week_info.rb) where weekend days were incorrectly checked using "Sa" and "Su".
|
26
|
+
- Handled empty `@time_block` in [`TimeBlockChart#initialize`](lib/timet/time_block_chart.rb) to prevent errors.
|
27
|
+
|
1
28
|
## [1.5.6] - 2025-04-07
|
2
29
|
|
3
30
|
**Improvements:**
|
data/lib/timet/application.rb
CHANGED
@@ -49,21 +49,27 @@ module Timet
|
|
49
49
|
|
50
50
|
BUCKET = 'timet'
|
51
51
|
|
52
|
+
def initialize(*args)
|
53
|
+
super
|
54
|
+
config = args[2] || {} # Third argument is the config hash
|
55
|
+
initialize_database(config)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Initializes the database connection based on the provided options.
|
59
|
+
#
|
60
|
+
# This method determines how to initialize the database connection based on the options provided.
|
61
|
+
# It supports injecting a database instance for testing, using a fallback for RSpec, and initializing
|
62
|
+
# a production database based on valid command arguments.
|
63
|
+
#
|
64
|
+
# @param options [Hash] A hash of options that may include a :database key for injecting a database instance.
|
65
|
+
# @option options [Database] :database An instance of the Database class to be used directly.
|
66
|
+
# @option options [Hash] :current_command The current command being executed, used to validate production
|
67
|
+
# database initialization.
|
68
|
+
#
|
69
|
+
# @return [void] This method does not return a value; it initializes the `@db` instance variable.
|
70
|
+
#
|
71
|
+
# @raise [SystemExit] If invalid arguments are provided for production database initialization.
|
52
72
|
no_commands do
|
53
|
-
# Initializes the database connection based on the provided options.
|
54
|
-
#
|
55
|
-
# This method determines how to initialize the database connection based on the options provided.
|
56
|
-
# It supports injecting a database instance for testing, using a fallback for RSpec, and initializing
|
57
|
-
# a production database based on valid command arguments.
|
58
|
-
#
|
59
|
-
# @param options [Hash] A hash of options that may include a :database key for injecting a database instance.
|
60
|
-
# @option options [Database] :database An instance of the Database class to be used directly.
|
61
|
-
# @option options [Hash] :current_command The current command being executed, used to validate production
|
62
|
-
# database initialization.
|
63
|
-
#
|
64
|
-
# @return [void] This method does not return a value; it initializes the `@db` instance variable.
|
65
|
-
#
|
66
|
-
# @raise [SystemExit] If invalid arguments are provided for production database initialization.
|
67
73
|
def initialize_database(options)
|
68
74
|
db_from_options = options[:database]
|
69
75
|
|
@@ -86,12 +92,6 @@ module Timet
|
|
86
92
|
end
|
87
93
|
end
|
88
94
|
|
89
|
-
def initialize(*args)
|
90
|
-
super
|
91
|
-
options = args[2] || {} # Third argument is the options hash
|
92
|
-
initialize_database(options)
|
93
|
-
end
|
94
|
-
|
95
95
|
desc "start [tag] --notes='' --pomodoro=[min]",
|
96
96
|
'Start time tracking for a task labeled with the provided [tag], notes and "pomodoro time"
|
97
97
|
in minutes (optional).
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Timet
|
4
|
+
#
|
5
|
+
# The BlockCharHelper module provides a utility method for getting the block character
|
6
|
+
# based on a given value.
|
7
|
+
#
|
8
|
+
module BlockCharHelper
|
9
|
+
# Character mapping for different time ranges
|
10
|
+
CHAR_MAPPING = {
|
11
|
+
0..120 => '_',
|
12
|
+
121..450 => ' ',
|
13
|
+
451..900 => '▂',
|
14
|
+
901..1350 => '▃',
|
15
|
+
1351..1800 => '▄',
|
16
|
+
1801..2250 => '▅',
|
17
|
+
2251..2700 => '▆',
|
18
|
+
2701..3150 => '▇',
|
19
|
+
3151..3600 => '█'
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
# Gets the block character for a given value
|
23
|
+
#
|
24
|
+
# @param [Integer, nil] value The value
|
25
|
+
# @return [String] The block character
|
26
|
+
def self.get_block_char(value)
|
27
|
+
return ' ' unless value
|
28
|
+
|
29
|
+
mapping = CHAR_MAPPING.find { |range, _| range.include?(value) }
|
30
|
+
mapping ? mapping.last : ' '
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Timet
|
4
|
+
# Helper methods for fetching item data.
|
5
|
+
module ItemDataHelper
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# Fetches the start time of a tracking item.
|
9
|
+
#
|
10
|
+
# @param item [Array] The tracking item.
|
11
|
+
#
|
12
|
+
# @return [Integer] The start time in epoch format.
|
13
|
+
#
|
14
|
+
# @example Fetch the start time of a tracking item
|
15
|
+
# fetch_item_start(item)
|
16
|
+
def fetch_item_start(item)
|
17
|
+
item[Timet::Application::FIELD_INDEX['start']]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Fetches the end time of a tracking item.
|
21
|
+
#
|
22
|
+
# @param item [Array] The tracking item.
|
23
|
+
#
|
24
|
+
# @return [Integer] The end time in epoch format.
|
25
|
+
#
|
26
|
+
# @example Fetch the end time of a tracking item
|
27
|
+
# fetch_item_end(item)
|
28
|
+
def fetch_item_end(item)
|
29
|
+
item[Timet::Application::FIELD_INDEX['end']] || TimeHelper.current_timestamp
|
30
|
+
end
|
31
|
+
|
32
|
+
# Fetches the end time of the tracking item before the current one.
|
33
|
+
#
|
34
|
+
# @param db [Timet::Database] The database instance.
|
35
|
+
# @param id [Integer] The ID of the current tracking item.
|
36
|
+
# @param item_start [Integer] The start time of the current tracking item.
|
37
|
+
#
|
38
|
+
# @return [Integer] The end time of the previous tracking item in epoch format.
|
39
|
+
#
|
40
|
+
# @example Fetch the end time of the previous tracking item
|
41
|
+
# fetch_item_before_end(db, 1, 1633072800)
|
42
|
+
def fetch_item_before_end(db, id, item_start)
|
43
|
+
db.find_item(id - 1)&.dig(Timet::Application::FIELD_INDEX['end']) || item_start
|
44
|
+
end
|
45
|
+
|
46
|
+
# Fetches the start time of the tracking item after the current one.
|
47
|
+
#
|
48
|
+
# @param db [Timet::Database] The database instance.
|
49
|
+
# @param id [Integer] The ID of the current tracking item.
|
50
|
+
#
|
51
|
+
# @return [Integer] The start time of the next tracking item in epoch format.
|
52
|
+
#
|
53
|
+
# @example Fetch the start time of the next tracking item
|
54
|
+
# fetch_item_after_start(db, id)
|
55
|
+
def fetch_item_after_start(db, id)
|
56
|
+
db.find_item(id + 1)&.dig(Timet::Application::FIELD_INDEX['start']) || TimeHelper.current_timestamp
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/timet/table.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
# require_relative 'color_codes'
|
4
4
|
require 'timet/time_report_helper'
|
5
|
+
require_relative 'utils'
|
5
6
|
module Timet
|
6
7
|
# This class is responsible for formatting the output of the `timet` application.
|
7
8
|
# It provides methods for formatting the table header, separators, and rows.
|
@@ -123,7 +124,7 @@ module Timet
|
|
123
124
|
|
124
125
|
block_hour = TimeHelper.count_seconds_per_hour_block(start_time, end_time, tag)
|
125
126
|
date_line = TimeHelper.timestamp_to_date(start_time)
|
126
|
-
time_block[date_line] = add_hashes(time_block[date_line], block_hour)
|
127
|
+
time_block[date_line] = Timet::Utils.add_hashes(time_block[date_line], block_hour)
|
127
128
|
time_block
|
128
129
|
end
|
129
130
|
|
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'week_info'
|
4
|
+
require_relative 'block_char_helper'
|
5
|
+
|
3
6
|
module Timet
|
4
7
|
#
|
5
8
|
# The TimeBlockChart class is responsible for generating and printing a visual representation
|
@@ -18,22 +21,18 @@ module Timet
|
|
18
21
|
# @attr_reader [Integer] start_hour The starting hour of the time block
|
19
22
|
# @attr_reader [Integer] end_hour The ending hour of the time block
|
20
23
|
class TimeBlockChart
|
21
|
-
# Character mapping for different time ranges
|
22
|
-
CHAR_MAPPING = {
|
23
|
-
0..120 => '_',
|
24
|
-
121..450 => '▁',
|
25
|
-
451..900 => '▂',
|
26
|
-
901..1350 => '▃',
|
27
|
-
1351..1800 => '▄',
|
28
|
-
1801..2250 => '▅',
|
29
|
-
2251..2700 => '▆',
|
30
|
-
2701..3150 => '▇',
|
31
|
-
3151..3600 => '█'
|
32
|
-
}.freeze
|
33
|
-
|
34
24
|
# Separator character for the chart
|
35
25
|
SEPARATOR_CHAR = '░'
|
36
26
|
|
27
|
+
# Width of the date/week string content (e.g., "02 2023-10-01 Fri")
|
28
|
+
DATE_WEEK_CONTENT_WIDTH = 17
|
29
|
+
|
30
|
+
# Width of the '┆- ' part after the date/week string
|
31
|
+
DATE_WEEK_BORDER_WIDTH = 3
|
32
|
+
|
33
|
+
# Width of the total hours column including the border
|
34
|
+
TOTAL_HOURS_COLUMN_WIDTH = 4
|
35
|
+
|
37
36
|
# Initializes a new TimeBlockChart instance.
|
38
37
|
#
|
39
38
|
# This method sets up the time block chart by processing the time entries from the provided table
|
@@ -50,8 +49,13 @@ module Timet
|
|
50
49
|
# @see Table#process_time_entries
|
51
50
|
def initialize(table)
|
52
51
|
@time_block = table.process_time_entries(display: false)
|
53
|
-
|
54
|
-
|
52
|
+
hours = @time_block.values.map(&:keys).flatten.uniq
|
53
|
+
if hours.empty?
|
54
|
+
@start_hour = @end_hour = 0
|
55
|
+
return
|
56
|
+
end
|
57
|
+
@start_hour = hours.min.to_i
|
58
|
+
@end_hour = hours.max.to_i
|
55
59
|
end
|
56
60
|
|
57
61
|
# Prints the time block chart.
|
@@ -59,13 +63,14 @@ module Timet
|
|
59
63
|
# This method formats and prints the time block chart, including the header and the time blocks
|
60
64
|
# for each entry. The chart is color-coded based on the provided color mapping for different tags.
|
61
65
|
#
|
62
|
-
# @param table [Hash] The time block data to be displayed in the chart.
|
63
66
|
# @param colors [Hash] A mapping of tags to colors, used to color-code the time blocks.
|
64
67
|
# @return [void] This method does not return a value; it performs side effects such as printing the chart.
|
65
68
|
#
|
66
69
|
# @example Print a time block chart
|
70
|
+
# # Assuming 'table' is an instance of Timet::Table
|
67
71
|
# chart = TimeBlockChart.new(table)
|
68
|
-
#
|
72
|
+
# colors = { "work" => 0, "break" => 1 } # Example color mapping
|
73
|
+
# chart.print_time_block_chart(colors)
|
69
74
|
#
|
70
75
|
# @note
|
71
76
|
# - The method first prints the header of the chart, which includes the time range.
|
@@ -74,9 +79,11 @@ module Timet
|
|
74
79
|
#
|
75
80
|
# @see #print_header
|
76
81
|
# @see #print_blocks
|
77
|
-
def print_time_block_chart(
|
82
|
+
def print_time_block_chart(colors)
|
83
|
+
return puts 'No time-block data to display.' if @no_data
|
84
|
+
|
78
85
|
print_header
|
79
|
-
print_blocks(
|
86
|
+
print_blocks(colors)
|
80
87
|
end
|
81
88
|
|
82
89
|
private
|
@@ -86,48 +93,64 @@ module Timet
|
|
86
93
|
# @return [void]
|
87
94
|
def print_header
|
88
95
|
puts
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
96
|
+
print_hours_row
|
97
|
+
# Dynamically build header using width constants
|
98
|
+
left = "┌#{'╴' * (DATE_WEEK_CONTENT_WIDTH - 11)}"
|
99
|
+
week = 'W'.center(DATE_WEEK_BORDER_WIDTH, '╴')
|
100
|
+
clock = '⏰'.center(DATE_WEEK_BORDER_WIDTH + 4, '╴')
|
101
|
+
hours_width = (@end_hour - @start_hour + 1) * 4
|
102
|
+
hours = '╴' * hours_width
|
103
|
+
right = "#{'╴' * (TOTAL_HOURS_COLUMN_WIDTH - 1)}┼"
|
104
|
+
header = left.gray + week.gray + clock.gray + '┼'.gray + hours.gray + right.gray
|
105
|
+
puts header
|
93
106
|
end
|
94
107
|
|
95
|
-
# Prints the
|
108
|
+
# Prints the hours row in the header.
|
96
109
|
#
|
97
|
-
#
|
98
|
-
|
99
|
-
|
110
|
+
# @return [void]
|
111
|
+
def print_hours_row
|
112
|
+
left_margin = DATE_WEEK_CONTENT_WIDTH + DATE_WEEK_BORDER_WIDTH - 1
|
113
|
+
print ' ' * left_margin
|
114
|
+
(@start_hour..@end_hour).each { |hour| print format('%02d', hour).rjust(4) }
|
115
|
+
puts
|
116
|
+
end
|
117
|
+
|
118
|
+
# Prints the main body of the time block chart, including date rows and corresponding time blocks.
|
100
119
|
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
120
|
+
# This method iterates over each date present in the `@time_block` instance variable,
|
121
|
+
# which is expected to be a hash mapping date strings to hourly time block data.
|
122
|
+
# For each date, it:
|
123
|
+
# 1. Displays week and date information using `WeekInfo`.
|
124
|
+
# 2. Prints the visual time blocks for the hours of the day using `#print_time_blocks`.
|
125
|
+
# 3. Calculates and prints the total hours logged for that day using `#calculate_and_print_hours`.
|
126
|
+
# After processing all dates, it prints a chart footer using `#print_footer`.
|
105
127
|
#
|
106
|
-
# @
|
107
|
-
#
|
108
|
-
#
|
128
|
+
# @param colors [Hash] A mapping of tags to color indices. This is used by `#print_time_blocks`
|
129
|
+
# to color-code the visual time blocks. Example: `{ "work" => 0, "break" => 1 }`
|
130
|
+
# @return [void] This method does not return a value; it prints directly to the console.
|
109
131
|
#
|
110
132
|
# @note
|
111
|
-
# -
|
112
|
-
# -
|
113
|
-
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
# - A footer is printed at the end to provide a visual separation.
|
133
|
+
# - This is a private method, primarily called by `#print_time_block_chart`.
|
134
|
+
# - It returns early if `@time_block` is `nil`. If `@time_block` is an empty hash,
|
135
|
+
# no date-specific rows are printed, but the chart footer is still rendered.
|
136
|
+
# - Relies on `@start_hour` and `@end_hour` instance variables being correctly set
|
137
|
+
# during the `TimeBlockChart`'s initialization.
|
117
138
|
#
|
118
|
-
# @see #format_and_print_date_info
|
139
|
+
# @see WeekInfo#format_and_print_date_info
|
119
140
|
# @see #print_time_blocks
|
120
141
|
# @see #calculate_and_print_hours
|
121
142
|
# @see #print_footer
|
122
|
-
def print_blocks(
|
123
|
-
return unless
|
143
|
+
def print_blocks(colors)
|
144
|
+
return unless @time_block
|
124
145
|
|
125
146
|
weeks = []
|
126
|
-
@time_block.
|
127
|
-
|
128
|
-
day =
|
147
|
+
@time_block.keys.sort.each do |date_string| # ISO-date strings sort naturally
|
148
|
+
date_object = Date.parse(date_string)
|
149
|
+
day = date_object.strftime('%a')[0..2]
|
129
150
|
|
130
|
-
|
151
|
+
week_info = WeekInfo.new(date_object, date_string, weeks)
|
152
|
+
print_inter_week_separator if week_info.needs_inter_week_separator?
|
153
|
+
week_info.format_and_print_date_info(day)
|
131
154
|
|
132
155
|
time_block_initial = @time_block[date_string]
|
133
156
|
print_time_blocks(time_block_initial, colors)
|
@@ -148,62 +171,6 @@ module Timet
|
|
148
171
|
puts
|
149
172
|
end
|
150
173
|
|
151
|
-
# Formats and prints the date information
|
152
|
-
#
|
153
|
-
# @param [String] date_string The date string
|
154
|
-
# @param [String] day The day of the week
|
155
|
-
# @param [Array] weeks The list of weeks
|
156
|
-
# @return [void]
|
157
|
-
def format_and_print_date_info(date_string, day, weeks)
|
158
|
-
weekend = date_string
|
159
|
-
day = day.red if %w[Sa Su].include?(day)
|
160
|
-
weekend = weekend.red if %w[Sa Su].include?(day)
|
161
|
-
|
162
|
-
week = format_and_print_week(date_string, weeks)
|
163
|
-
|
164
|
-
print '┆'.gray + "#{week} #{weekend} #{day}" + '┆- '.gray
|
165
|
-
end
|
166
|
-
|
167
|
-
# Formats and prints the week information
|
168
|
-
#
|
169
|
-
# @param [String] date_string The date string
|
170
|
-
# @param [Array] weeks The list of weeks
|
171
|
-
# @return [String] The formatted week string
|
172
|
-
def format_and_print_week(date_string, weeks)
|
173
|
-
week, current_index = determine_week(date_string, weeks)
|
174
|
-
print_separator(week, current_index)
|
175
|
-
week
|
176
|
-
end
|
177
|
-
|
178
|
-
# Determines the week for a given date
|
179
|
-
#
|
180
|
-
# @param [String] date_string The date string
|
181
|
-
# @param [Array] weeks The list of weeks
|
182
|
-
# @return [Array] The week string and current index
|
183
|
-
def determine_week(date_string, weeks)
|
184
|
-
weeks << Date.parse(date_string).cweek
|
185
|
-
current_index = weeks.size - 1
|
186
|
-
current_week = weeks[current_index]
|
187
|
-
week = if current_week == weeks[current_index - 1] && current_index.positive?
|
188
|
-
' '
|
189
|
-
else
|
190
|
-
format('%02d', current_week).to_s.underline
|
191
|
-
end
|
192
|
-
[week, current_index]
|
193
|
-
end
|
194
|
-
|
195
|
-
# Prints the separator line
|
196
|
-
#
|
197
|
-
# @param [String] week The week string
|
198
|
-
# @param [Integer] current_index The current index
|
199
|
-
# @return [void]
|
200
|
-
def print_separator(week, current_index)
|
201
|
-
return unless week != ' ' && current_index.positive?
|
202
|
-
|
203
|
-
sep = SEPARATOR_CHAR
|
204
|
-
puts "┆#{sep * 17}┼#{sep * (@end_hour - @start_hour + 1) * 4}#{sep * 3}┼#{sep * 4}".gray
|
205
|
-
end
|
206
|
-
|
207
174
|
# Prints the footer of the chart
|
208
175
|
#
|
209
176
|
# @return [void]
|
@@ -234,7 +201,7 @@ module Timet
|
|
234
201
|
formatted_hour = format('%02d', hour)
|
235
202
|
hour_data = time_block_initial[formatted_hour]
|
236
203
|
tag = hour_data&.last
|
237
|
-
[tag, get_block_char(hour_data&.first)]
|
204
|
+
[tag, BlockCharHelper.get_block_char(hour_data&.first)]
|
238
205
|
end
|
239
206
|
|
240
207
|
# Prints the colored block character
|
@@ -250,14 +217,21 @@ module Timet
|
|
250
217
|
print colored_block.rjust(4)
|
251
218
|
end
|
252
219
|
|
253
|
-
#
|
220
|
+
# Prints the separator line between different weeks in the chart.
|
254
221
|
#
|
255
|
-
# @
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
222
|
+
# @return [void]
|
223
|
+
def print_inter_week_separator
|
224
|
+
sep = SEPARATOR_CHAR
|
225
|
+
line_parts = [
|
226
|
+
'┆',
|
227
|
+
sep * DATE_WEEK_CONTENT_WIDTH, # Date/Week column fill
|
228
|
+
'┼',
|
229
|
+
sep * (@end_hour - @start_hour + 1) * 4, # Hour blocks column fill
|
230
|
+
sep * DATE_WEEK_BORDER_WIDTH, # Additional fill for hour blocks column
|
231
|
+
'┼',
|
232
|
+
sep * TOTAL_HOURS_COLUMN_WIDTH # Total hours column fill
|
233
|
+
]
|
234
|
+
puts line_parts.join.gray
|
261
235
|
end
|
262
236
|
end
|
263
237
|
end
|
data/lib/timet/time_helper.rb
CHANGED
@@ -247,5 +247,22 @@ module Timet
|
|
247
247
|
def self.beginning_of_day(time = Time.now)
|
248
248
|
Time.new(time.year, time.month, time.day)
|
249
249
|
end
|
250
|
+
|
251
|
+
# Creates a new datetime object.
|
252
|
+
#
|
253
|
+
# @param base_date_time [Time] The base date and time.
|
254
|
+
# @param parsed_time_component [Time] The parsed time component.
|
255
|
+
#
|
256
|
+
# @return [Time] The new datetime object.
|
257
|
+
def self.create_new_datetime(base_date_time, parsed_time_component)
|
258
|
+
Time.new(
|
259
|
+
base_date_time.year,
|
260
|
+
base_date_time.month,
|
261
|
+
base_date_time.day,
|
262
|
+
parsed_time_component.hour,
|
263
|
+
parsed_time_component.min,
|
264
|
+
parsed_time_component.sec
|
265
|
+
)
|
266
|
+
end
|
250
267
|
end
|
251
268
|
end
|
data/lib/timet/time_report.rb
CHANGED
@@ -81,16 +81,16 @@ module Timet
|
|
81
81
|
# - A time block chart is generated and printed using the `TimeBlockChart` class.
|
82
82
|
# - The tag distribution is calculated and displayed based on the unique colors assigned to tags.
|
83
83
|
#
|
84
|
-
# @see #table
|
85
|
-
# @see #print_time_block_chart
|
86
|
-
# @see #tag_distribution
|
84
|
+
# @see Timet::Table#table For table generation.
|
85
|
+
# @see Timet::TimeBlockChart#print_time_block_chart For chart generation.
|
86
|
+
# @see #tag_distribution For tag statistics display.
|
87
87
|
def display
|
88
88
|
return puts 'No tracked time found for the specified filter.' if @items.empty?
|
89
89
|
|
90
90
|
@table.table
|
91
91
|
colors = @items.map { |x| x[3] }.uniq.each_with_index.to_h
|
92
92
|
chart = TimeBlockChart.new(@table)
|
93
|
-
chart.print_time_block_chart(
|
93
|
+
chart.print_time_block_chart(colors)
|
94
94
|
tag_distribution(colors)
|
95
95
|
end
|
96
96
|
|
@@ -150,7 +150,7 @@ module Timet
|
|
150
150
|
# write_csv_rows(csv)
|
151
151
|
def write_csv_rows(csv)
|
152
152
|
items.each do |item|
|
153
|
-
csv << format_item(item)
|
153
|
+
csv << Timet::Utils.format_item(item)
|
154
154
|
end
|
155
155
|
end
|
156
156
|
|
@@ -166,10 +166,10 @@ module Timet
|
|
166
166
|
#
|
167
167
|
# @note The method filters the items based on the specified date range and tag.
|
168
168
|
def filter_items(filter, tag)
|
169
|
-
if date_ranges.key?(filter)
|
170
|
-
start_date, end_date = date_ranges[filter]
|
169
|
+
if Timet::Utils.date_ranges.key?(filter)
|
170
|
+
start_date, end_date = Timet::Utils.date_ranges[filter]
|
171
171
|
filter_by_date_range(start_date, end_date, tag)
|
172
|
-
elsif valid_date_format?(filter)
|
172
|
+
elsif Timet::Utils.valid_date_format?(filter)
|
173
173
|
start_date, end_date = filter.split('..').map { |x| Date.parse(x) }
|
174
174
|
filter_by_date_range(start_date, end_date, tag)
|
175
175
|
else
|
@@ -226,7 +226,7 @@ module Timet
|
|
226
226
|
return key if values.include?(filter)
|
227
227
|
end
|
228
228
|
|
229
|
-
return filter if filter && valid_date_format?(filter)
|
229
|
+
return filter if filter && Timet::Utils.valid_date_format?(filter)
|
230
230
|
|
231
231
|
'today'
|
232
232
|
end
|