timet 1.3.1 → 1.4.0
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 +59 -0
- data/README.md +7 -24
- data/lib/timet/application.rb +19 -36
- data/lib/timet/application_helper.rb +36 -5
- data/lib/timet/color_codes.rb +61 -0
- data/lib/timet/database.rb +20 -14
- data/lib/timet/table.rb +300 -0
- data/lib/timet/tag_distribution.rb +61 -0
- data/lib/timet/time_block_chart.rb +239 -0
- data/lib/timet/time_report.rb +20 -44
- data/lib/timet/time_report_helper.rb +0 -66
- data/lib/timet/validation_edit_helper.rb +1 -1
- data/lib/timet/version.rb +2 -2
- data/lib/timet.rb +1 -13
- metadata +6 -3
- data/lib/timet/formatter.rb +0 -298
data/lib/timet/table.rb
ADDED
@@ -0,0 +1,300 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Timet
|
4
|
+
# This module is responsible for formatting the output of the `timet` application.
|
5
|
+
# It provides methods for formatting the table header, separators, and rows.
|
6
|
+
module Table
|
7
|
+
# Generates and displays a table summarizing time entries, including headers, time blocks, and total durations.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# table
|
11
|
+
#
|
12
|
+
# @return [Array<(String, Hash)>] An array containing the time block string and a hash of durations by tag.
|
13
|
+
#
|
14
|
+
# @note
|
15
|
+
# - The method relies on the `header`, `process_time_entries`, `separator`, and `total` methods.
|
16
|
+
# - The `header` method is responsible for printing the table header.
|
17
|
+
# - The `process_time_entries` method processes the time entries and returns the time block and duration by tag.
|
18
|
+
# - The `separator` method returns a string representing the separator line.
|
19
|
+
# - The `total` method prints the total duration.
|
20
|
+
#
|
21
|
+
# @see #header
|
22
|
+
# @see #process_time_entries
|
23
|
+
# @see #separator
|
24
|
+
# @see #total
|
25
|
+
def table
|
26
|
+
header
|
27
|
+
time_block, duration_by_tag = process_time_entries
|
28
|
+
puts separator
|
29
|
+
total
|
30
|
+
[time_block, duration_by_tag]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Formats the header of the time tracking report table.
|
34
|
+
#
|
35
|
+
# @return [void] This method does not return a value; it performs side effects such as printing
|
36
|
+
# the formatted header.
|
37
|
+
#
|
38
|
+
# @example Format and print the table header
|
39
|
+
# header
|
40
|
+
#
|
41
|
+
# @note The method constructs a string representing the table header and prints it.
|
42
|
+
def header
|
43
|
+
title = "Tracked time report [#{@filter.blink.red}]:"
|
44
|
+
header = <<~TABLE
|
45
|
+
#{title}
|
46
|
+
#{separator}
|
47
|
+
\033[32m| Id | Date | Tag | Start | End | Duration | Notes |\033[0m
|
48
|
+
#{separator}
|
49
|
+
TABLE
|
50
|
+
puts header
|
51
|
+
end
|
52
|
+
|
53
|
+
# Formats the separator line for the time tracking report table.
|
54
|
+
#
|
55
|
+
# @return [String] The formatted separator line.
|
56
|
+
#
|
57
|
+
# @example Get the formatted table separator
|
58
|
+
# separator # => '+-------+------------+--------+----------+----------+----------+------------+'
|
59
|
+
#
|
60
|
+
# @note The method returns a string representing the separator line for the table.
|
61
|
+
def separator
|
62
|
+
'+-------+------------+--------+----------+----------+----------+--------------------+'
|
63
|
+
end
|
64
|
+
|
65
|
+
# Processes each time entry in the `items` array and updates the time block and duration by tag.
|
66
|
+
#
|
67
|
+
# @return [Array<(Hash, Hash)>] An array containing the updated time block and duration by tag.
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# items = [
|
71
|
+
# [start_time1, end_time1, tag1],
|
72
|
+
# [start_time2, end_time2, tag2]
|
73
|
+
# ]
|
74
|
+
# process_time_entries
|
75
|
+
# #=> [{ '2024-10-21' => { 8 => [duration1, tag1], 9 => [duration2, tag2] } },
|
76
|
+
# { tag1 => total_duration1, tag2 => total_duration2 }]
|
77
|
+
#
|
78
|
+
# @note
|
79
|
+
# - The method relies on the `items` instance variable, which should be an array of arrays.
|
80
|
+
# - Each sub-array in `items` is expected to contain a start time, end time, and a tag.
|
81
|
+
# - The `display_time_entry` method is used to display each time entry.
|
82
|
+
# - The `process_time_block_item` method processes each time entry and updates the time block and duration by tag.
|
83
|
+
#
|
84
|
+
# @see #items
|
85
|
+
# @see #display_time_entry
|
86
|
+
# @see #process_time_block_item
|
87
|
+
def process_time_entries
|
88
|
+
duration_by_tag = Hash.new(0)
|
89
|
+
time_block = Hash.new { |hash, key| hash[key] = {} }
|
90
|
+
|
91
|
+
items.each_with_index do |item, idx|
|
92
|
+
display_time_entry(item, TimeHelper.extract_date(items, idx))
|
93
|
+
time_block, duration_by_tag = process_time_block_item(item, time_block, duration_by_tag)
|
94
|
+
end
|
95
|
+
[time_block, duration_by_tag]
|
96
|
+
end
|
97
|
+
|
98
|
+
# Processes a time block item and updates the time block hash.
|
99
|
+
#
|
100
|
+
# @param item [Array] The time entry to process, containing the start time, end time, and tag.
|
101
|
+
# @param time_block [Hash] A hash containing time block data, where keys are dates and values are hashes of time
|
102
|
+
# slots and their corresponding values.
|
103
|
+
# @param duration_by_tag [Hash] A hash containing the total duration by tag.
|
104
|
+
#
|
105
|
+
# @return [Array<(Hash, Hash)>] An array containing the updated time block hash and the updated duration
|
106
|
+
# by tag hash.
|
107
|
+
#
|
108
|
+
# @example
|
109
|
+
# item = [nil, Time.new(2024, 10, 21, 8, 0, 0), Time.new(2024, 10, 21, 9, 0, 0), 'work']
|
110
|
+
# time_block = {}
|
111
|
+
# duration_by_tag = {}
|
112
|
+
# process_time_block_item(item, time_block, duration_by_tag)
|
113
|
+
# #=> [{ '2024-10-21' => { 8 => [3600, 'work'] } }, { 'work' => 3600 }]
|
114
|
+
#
|
115
|
+
# @note
|
116
|
+
# - The method relies on the `TimeHelper` module for time-related calculations.
|
117
|
+
# - The `add_hashes` method is used to merge the new time block data into the existing time block hash.
|
118
|
+
# - The `calculate_duration` method calculates the duration between the start and end times.
|
119
|
+
#
|
120
|
+
# @see TimeHelper#count_seconds_per_hour_block
|
121
|
+
# @see TimeHelper#timestamp_to_date
|
122
|
+
# @see TimeHelper#calculate_duration
|
123
|
+
# @see #add_hashes
|
124
|
+
def process_time_block_item(item, time_block, duration_by_tag)
|
125
|
+
_, start_time, end_time, tag = item
|
126
|
+
|
127
|
+
block_hour = TimeHelper.count_seconds_per_hour_block(start_time, end_time, tag)
|
128
|
+
date_line = TimeHelper.timestamp_to_date(start_time)
|
129
|
+
time_block[date_line] = add_hashes(time_block[date_line], block_hour)
|
130
|
+
duration_by_tag[tag] += TimeHelper.calculate_duration(start_time, end_time)
|
131
|
+
[time_block, duration_by_tag]
|
132
|
+
end
|
133
|
+
|
134
|
+
# Displays a single time entry in the report.
|
135
|
+
#
|
136
|
+
# @param item [Array] The item to display.
|
137
|
+
# @param date [String, nil] The date to display. If nil, the date is not displayed.
|
138
|
+
#
|
139
|
+
# @return [void] This method does not return a value; it performs side effects such as printing the row.
|
140
|
+
#
|
141
|
+
# @example Display a time entry
|
142
|
+
# display_time_entry(item, '2021-10-01')
|
143
|
+
#
|
144
|
+
# @note The method formats and prints the row for the time entry.
|
145
|
+
def display_time_entry(item, date = nil)
|
146
|
+
return puts 'Missing time entry data.' unless item
|
147
|
+
|
148
|
+
id, start_time_value, end_time_value, tag_name, notes = item
|
149
|
+
duration = TimeHelper.calculate_duration(start_time_value, end_time_value)
|
150
|
+
start_time = TimeHelper.format_time(start_time_value)
|
151
|
+
end_time = TimeHelper.format_time(end_time_value)
|
152
|
+
start_date = date || (' ' * 10)
|
153
|
+
puts format_table_row(id, tag_name[0..5], start_date, start_time, end_time, duration, notes)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Formats a table row with the given row data.
|
157
|
+
#
|
158
|
+
# @param row [Array] The row data to format, containing the following elements:
|
159
|
+
# - id [Integer] The ID of the time entry.
|
160
|
+
# - tag [String] The tag associated with the time entry.
|
161
|
+
# - start_date [String] The start date of the time entry.
|
162
|
+
# - start_time [String] The start time of the time entry.
|
163
|
+
# - end_time [String] The end time of the time entry.
|
164
|
+
# - duration [Integer] The duration of the time entry in seconds.
|
165
|
+
# - notes [String] Any notes associated with the time entry.
|
166
|
+
#
|
167
|
+
# @return [String] The formatted table row.
|
168
|
+
#
|
169
|
+
# @example
|
170
|
+
# row = [1, 'work', '2024-10-21', '08:00:00', '09:00:00', 3600, 'Completed task A']
|
171
|
+
# format_table_row(*row)
|
172
|
+
# #=> "| 1| 2024-10-21 | work | 08:00:00 | 09:00:00 | 1:00:00 | Completed task A |"
|
173
|
+
#
|
174
|
+
# @note
|
175
|
+
# - The method relies on the `@db` instance variable, which should be an object with `find_item`
|
176
|
+
# and `seconds_to_hms` methods.
|
177
|
+
# - The `format_end_time`, `format_mark`, and `format_notes` methods are used to format specific parts of the row.
|
178
|
+
#
|
179
|
+
# @see #format_end_time
|
180
|
+
# @see #format_mark
|
181
|
+
# @see #format_notes
|
182
|
+
def format_table_row(*row)
|
183
|
+
id, tag, start_date, start_time, end_time, duration, notes = row
|
184
|
+
end_time = format_end_time(end_time, id, duration)
|
185
|
+
mark = format_mark(id)
|
186
|
+
|
187
|
+
"| #{id.to_s.rjust(6)}| #{start_date} | #{tag.ljust(6)} | #{start_time.split[1]} | " \
|
188
|
+
"#{end_time.rjust(8)} | #{@db.seconds_to_hms(duration).rjust(8)} | #{format_notes(notes)} #{mark}"
|
189
|
+
end
|
190
|
+
|
191
|
+
# Formats the end time of the time entry.
|
192
|
+
#
|
193
|
+
# @param end_time [String] The end time of the time entry.
|
194
|
+
# @param id [Integer] The ID of the time entry.
|
195
|
+
# @param duration [Integer] The duration of the time entry in seconds.
|
196
|
+
#
|
197
|
+
# @return [String] The formatted end time.
|
198
|
+
#
|
199
|
+
# @example
|
200
|
+
# format_end_time('09:00:00', 1, 3600)
|
201
|
+
# #=> "09:00:00"
|
202
|
+
#
|
203
|
+
# @note
|
204
|
+
# - The method relies on the `@db` instance variable, which should be an object with a `find_item` method.
|
205
|
+
# - If the `pomodoro` value is positive and the end time is not set, a blinking `timet` is added.
|
206
|
+
#
|
207
|
+
# @see #format_table_row
|
208
|
+
def format_end_time(end_time, id, duration)
|
209
|
+
end_time = end_time ? end_time.split[1] : '-'
|
210
|
+
pomodoro = @db.find_item(id)[5] || 0
|
211
|
+
|
212
|
+
if pomodoro.positive? && end_time == '-'
|
213
|
+
delta = (@db.find_item(id)[5] - (duration / 60.0)).round(1)
|
214
|
+
timet = "\e]8;;Session ends\a#{delta} min\e]8;;\a".green
|
215
|
+
end_time = " #{timet}".blink
|
216
|
+
end
|
217
|
+
|
218
|
+
end_time
|
219
|
+
end
|
220
|
+
|
221
|
+
# Formats the mark for the time entry.
|
222
|
+
#
|
223
|
+
# @param id [Integer] The ID of the time entry.
|
224
|
+
#
|
225
|
+
# @return [String] The formatted mark.
|
226
|
+
#
|
227
|
+
# @example
|
228
|
+
# format_mark(1)
|
229
|
+
# #=> "|"
|
230
|
+
#
|
231
|
+
# @note
|
232
|
+
# - The method relies on the `@db` instance variable, which should be an object with a `find_item` method.
|
233
|
+
# - If the `pomodoro` value is positive, a special mark is added.
|
234
|
+
#
|
235
|
+
# @see #format_table_row
|
236
|
+
def format_mark(id)
|
237
|
+
pomodoro = @db.find_item(id)[5] || 0
|
238
|
+
mark = '|'
|
239
|
+
mark = "#{'├'.white} #{'P'.blue.blink}" if pomodoro.positive?
|
240
|
+
mark
|
241
|
+
end
|
242
|
+
|
243
|
+
# Formats the notes column of the time tracking report table.
|
244
|
+
#
|
245
|
+
# @param notes [String, nil] The notes to be formatted.
|
246
|
+
# @return [String] The formatted notes.
|
247
|
+
#
|
248
|
+
# @example Format notes
|
249
|
+
# format_notes('This is a long note that needs to be truncated')
|
250
|
+
#
|
251
|
+
# @note The method truncates the notes to a maximum of 20 characters and pads them to a fixed width.
|
252
|
+
def format_notes(notes)
|
253
|
+
spaces = 17
|
254
|
+
return ' ' * spaces unless notes
|
255
|
+
|
256
|
+
max_length = spaces - 3
|
257
|
+
notes = "#{notes.slice(0, max_length)}..." if notes.length > max_length
|
258
|
+
notes.ljust(spaces)
|
259
|
+
end
|
260
|
+
|
261
|
+
# Displays the total duration of the tracked time entries.
|
262
|
+
#
|
263
|
+
# @return [void] This method does not return a value; it performs side effects such as printing the total duration.
|
264
|
+
#
|
265
|
+
# @example Display the total duration
|
266
|
+
# total
|
267
|
+
#
|
268
|
+
# @note The method calculates and prints the total duration of the tracked time entries.
|
269
|
+
def total
|
270
|
+
total = @items.map do |item|
|
271
|
+
TimeHelper.calculate_duration(item[1], item[2])
|
272
|
+
end.sum
|
273
|
+
puts "|#{' ' * 43}#{'Total:'.blue} | #{@db.seconds_to_hms(total).rjust(8).blue} |#{' ' * 20}|"
|
274
|
+
puts separator
|
275
|
+
display_pomodoro_label
|
276
|
+
end
|
277
|
+
|
278
|
+
# Displays a blinking "Pomodoro" label if the sum of the compacted values in the 6th column of @items is positive.
|
279
|
+
#
|
280
|
+
# @example
|
281
|
+
# display_pomodoro_label
|
282
|
+
#
|
283
|
+
# @return [void] This method returns nothing.
|
284
|
+
#
|
285
|
+
# @note
|
286
|
+
# - The method relies on the `@items` instance variable, which should be an array of arrays.
|
287
|
+
# - The 6th column of each sub-array in `@items` is expected to contain numeric values.
|
288
|
+
# - The method uses the `blue.blink` color formatting, which assumes the presence of a `String` extension or
|
289
|
+
# gem that supports color formatting.
|
290
|
+
#
|
291
|
+
# @see #@items
|
292
|
+
# @see String#blue
|
293
|
+
# @see String#blink
|
294
|
+
def display_pomodoro_label
|
295
|
+
return unless @items.map { |x| x[5] }.compact.sum.positive?
|
296
|
+
|
297
|
+
puts "#{'P'.blue.blink}omodoro"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Timet
|
4
|
+
# The TagDistribution module provides functionality to format and display the distribution of tags based on their
|
5
|
+
# durations. This is particularly useful for visualizing how time is distributed across different tags in a project
|
6
|
+
# or task management system.
|
7
|
+
module TagDistribution
|
8
|
+
MAX_BAR_LENGTH = 70
|
9
|
+
BLOCK_CHAR = '▅'
|
10
|
+
TAG_SIZE = 12
|
11
|
+
|
12
|
+
# Formats and displays the tag distribution.
|
13
|
+
#
|
14
|
+
# @param duration_by_tag [Hash<String, Integer>] A hash where keys are tags and values are durations in seconds.
|
15
|
+
# @return [void] This method outputs the formatted tag distribution to the console.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# duration_by_tag = { "timet" => 3600, "nextjs" => 1800 }
|
19
|
+
# Formatter.format_tag_distribution(duration_by_tag)
|
20
|
+
# # Output:
|
21
|
+
# # timet: 66.67% ====================
|
22
|
+
# # nextjs: 33.33% ==========
|
23
|
+
def tag_distribution(duration_by_tag, colors)
|
24
|
+
total = duration_by_tag.values.sum
|
25
|
+
return unless total.positive?
|
26
|
+
|
27
|
+
sorted_duration_by_tag = duration_by_tag.sort_by { |_, duration| -duration }
|
28
|
+
process_and_print_tags(sorted_duration_by_tag, total, colors)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Processes and prints the tag distribution information.
|
32
|
+
#
|
33
|
+
# @param sorted_duration_by_tag [Array<Array(String, Numeric)>] An array of arrays where each inner array contains a
|
34
|
+
# tag and its corresponding duration, sorted by duration in descending order.
|
35
|
+
# @param total [Numeric] The total duration of all tags combined.
|
36
|
+
# @return [void] This method outputs the tag distribution information to the standard output.
|
37
|
+
def process_and_print_tags(sorted_duration_by_tag, total, colors)
|
38
|
+
sorted_duration_by_tag.each do |tag, duration|
|
39
|
+
value, bar_length = calculate_value_and_bar_length(duration, total)
|
40
|
+
horizontal_bar = (BLOCK_CHAR * bar_length).to_s.color(colors[tag] + 1)
|
41
|
+
tag = tag[0..TAG_SIZE - 1] if tag.size >= TAG_SIZE
|
42
|
+
puts "#{tag.rjust(TAG_SIZE)}: #{value.to_s.rjust(5)}% #{horizontal_bar}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Calculates the percentage value and bar length for a given duration and total duration.
|
47
|
+
#
|
48
|
+
# @param duration [Numeric] The duration for the current tag.
|
49
|
+
# @param total [Numeric] The total duration.
|
50
|
+
# @return [Array<(Float, Integer)>] An array containing the calculated value and bar length.
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# calculate_value_and_bar_length(50, 100, 2) #=> [50.0, 25]
|
54
|
+
def calculate_value_and_bar_length(duration, total)
|
55
|
+
value = duration.to_f / total
|
56
|
+
percentage_value = (duration.to_f / total * 100).round(2)
|
57
|
+
bar_length = (value * MAX_BAR_LENGTH).round
|
58
|
+
[percentage_value, bar_length]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Timet
|
4
|
+
# This module is responsible for formatting the output of the `timet` application.
|
5
|
+
# It provides methods for formatting the table header, separators, and rows.
|
6
|
+
module TimeBlockChart
|
7
|
+
CHAR_MAPPING = {
|
8
|
+
0..120 => '_',
|
9
|
+
121..450 => '▁',
|
10
|
+
451..900 => '▂',
|
11
|
+
901..1350 => '▃',
|
12
|
+
1351..1800 => '▄',
|
13
|
+
1801..2250 => '▅',
|
14
|
+
2251..2700 => '▆',
|
15
|
+
2701..3150 => '▇',
|
16
|
+
3151..3600 => '█'
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
SEPARATOR_CHAR = '░'
|
20
|
+
|
21
|
+
# Prints a time block chart based on the provided time block and colors.
|
22
|
+
#
|
23
|
+
# @param time_block [Hash] A hash where the keys are time blocks and the values are hashes of time slots and their
|
24
|
+
# corresponding values.
|
25
|
+
# Example: { "block1" => { 10 => "value1", 11 => "value2" }, "block2" => { 12 => "value3" } }
|
26
|
+
# @param colors [Hash] A hash where the keys are time slots and the values are the colors to be used
|
27
|
+
# for those slots.
|
28
|
+
# Example: { 10 => "red", 11 => "blue", 12 => "green" }
|
29
|
+
#
|
30
|
+
# @return [void] This method does not return a value; it prints the chart directly to the output.
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# time_block = { "block1" => { 10 => "value1", 11 => "value2" }, "block2" => { 12 => "value3" } }
|
34
|
+
# colors = { 10 => "red", 11 => "blue", 12 => "green" }
|
35
|
+
# print_time_block_chart(time_block, colors)
|
36
|
+
#
|
37
|
+
# @note This method relies on two helper methods: `print_header` and `print_blocks`.
|
38
|
+
# Ensure these methods are defined and available in the scope where `print_time_block_chart` is called.
|
39
|
+
#
|
40
|
+
# @see #print_header
|
41
|
+
# @see #print_blocks
|
42
|
+
def print_time_block_chart(time_block, colors)
|
43
|
+
start_hour = time_block.values.map(&:keys).flatten.uniq.min.to_i
|
44
|
+
print_header(start_hour)
|
45
|
+
print_blocks(time_block, colors, start_hour)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Prints the header of the time block chart.
|
49
|
+
#
|
50
|
+
# The header includes a visual representation of the time slots from the given start time to 23.
|
51
|
+
# Each time slot is formatted as a two-digit number and aligned to the right within a fixed width.
|
52
|
+
#
|
53
|
+
# @param start_hour [Integer] The starting time for the chart. This should be an integer between 0 and 23.
|
54
|
+
#
|
55
|
+
# @return [void] This method does not return a value; it prints the header directly to the output.
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# print_header(10)
|
59
|
+
# # Output:
|
60
|
+
# #
|
61
|
+
# # ⏳ ↦ [ 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
|
62
|
+
#
|
63
|
+
# @note The method assumes that the start_hour is within the valid range of 0 to 23.
|
64
|
+
# If the start_hour is outside this range, the output may not be as expected.
|
65
|
+
def print_header(start_hour)
|
66
|
+
puts
|
67
|
+
print ' ' * 19
|
68
|
+
(start_hour..23).each { |hour| print format('%02d', hour).rjust(4) }
|
69
|
+
puts
|
70
|
+
puts '┌╴W ╴╴╴╴╴╴⏰╴╴╴╴╴╴┼'.gray + "#{'╴' * (24 - start_hour) * 4}╴╴╴┼".gray
|
71
|
+
end
|
72
|
+
|
73
|
+
# Prints the time blocks for each date in the given time block data structure.
|
74
|
+
#
|
75
|
+
# @param time_block [Hash] A hash where keys are date strings and values are time block data.
|
76
|
+
# @param colors [Hash] A hash containing color codes for formatting.
|
77
|
+
# @param start_hour [Integer] The starting hour for the time blocks.
|
78
|
+
# @return [void]
|
79
|
+
def print_blocks(time_block, colors, start_hour)
|
80
|
+
return unless time_block
|
81
|
+
|
82
|
+
weeks = []
|
83
|
+
time_block.each_key do |date_string|
|
84
|
+
date = Date.parse(date_string)
|
85
|
+
day = date.strftime('%a')[0..1]
|
86
|
+
|
87
|
+
format_and_print_date_info(date_string, day, weeks, start_hour)
|
88
|
+
|
89
|
+
time_block_initial = time_block[date_string]
|
90
|
+
print_time_blocks(start_hour, time_block_initial, colors)
|
91
|
+
|
92
|
+
calculate_and_print_hours(time_block_initial)
|
93
|
+
end
|
94
|
+
print_footer(start_hour)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Calculates the total hours from the given time block data and prints it.
|
98
|
+
#
|
99
|
+
# @param time_block_initial [Hash] A hash containing time block data for a specific date.
|
100
|
+
# @return [void]
|
101
|
+
def calculate_and_print_hours(time_block_initial)
|
102
|
+
total_seconds = time_block_initial.values.map { |item| item[0] }.sum
|
103
|
+
hours_per_day = (total_seconds / 3600.0).round(1)
|
104
|
+
print "-┆#{hours_per_day}h".gray
|
105
|
+
puts
|
106
|
+
end
|
107
|
+
|
108
|
+
# Formats and prints the date information including the week and day.
|
109
|
+
#
|
110
|
+
# @param date_string [String] The date string in a parsable format.
|
111
|
+
# @param day [String] The abbreviated day of the week (e.g., "Mo" for Monday).
|
112
|
+
# @param weeks [Array<Integer>] An array storing the week numbers.
|
113
|
+
# @param start_hour [Integer] The starting hour for the time blocks.
|
114
|
+
# @return [void]
|
115
|
+
def format_and_print_date_info(date_string, day, weeks, start_hour)
|
116
|
+
weekend = date_string
|
117
|
+
day = day.red if %w[Sa Su].include?(day)
|
118
|
+
weekend = weekend.red if %w[Sa Su].include?(day)
|
119
|
+
|
120
|
+
week = format_and_print_week(date_string, weeks, start_hour)
|
121
|
+
|
122
|
+
print '┆'.gray + "#{week} #{weekend} #{day} " + '┆- '.gray
|
123
|
+
end
|
124
|
+
|
125
|
+
# Formats and prints the week information including the separator if necessary.
|
126
|
+
#
|
127
|
+
# @param date_string [String] The date string in a parsable format.
|
128
|
+
# @param weeks [Array<Integer>] An array storing the week numbers.
|
129
|
+
# @param start_hour [Integer] The starting hour for the time blocks.
|
130
|
+
# @return [String] The formatted week string.
|
131
|
+
def format_and_print_week(date_string, weeks, start_hour)
|
132
|
+
week, current_index = determine_week(date_string, weeks)
|
133
|
+
print_separator(start_hour, week, current_index)
|
134
|
+
week
|
135
|
+
end
|
136
|
+
|
137
|
+
# Determines the week string based on the date and the previous week.
|
138
|
+
#
|
139
|
+
# @param date_string [String] The date string in a parsable format.
|
140
|
+
# @param weeks [Array<Integer>] An array storing the week numbers.
|
141
|
+
# @return [Array<String, Integer>] An array containing the formatted week string and the current index.
|
142
|
+
def determine_week(date_string, weeks)
|
143
|
+
weeks << Date.parse(date_string).cweek
|
144
|
+
current_index = weeks.size - 1
|
145
|
+
current_week = weeks[current_index]
|
146
|
+
week = current_week == weeks[current_index - 1] && current_index.positive? ? ' ' : current_week.to_s.underline
|
147
|
+
[week, current_index]
|
148
|
+
end
|
149
|
+
|
150
|
+
# Prints the separator line if the week string is not empty and the current index is positive.
|
151
|
+
#
|
152
|
+
# @param start_hour [Integer] The starting hour for the time blocks.
|
153
|
+
# @param week [String] The formatted week string.
|
154
|
+
# @param current_index [Integer] The current index in the weeks array.
|
155
|
+
# @return [void]
|
156
|
+
def print_separator(start_hour, week, current_index)
|
157
|
+
return unless week != ' ' && current_index.positive?
|
158
|
+
|
159
|
+
sep = SEPARATOR_CHAR
|
160
|
+
puts "┆#{sep * 17}┼#{sep * (24 - start_hour) * 4}#{sep * 3}┼#{sep * 4}".gray
|
161
|
+
end
|
162
|
+
|
163
|
+
# Prints the footer of the report.
|
164
|
+
#
|
165
|
+
# @param start_hour [Integer] The start time used to calculate the footer length.
|
166
|
+
# @return [void] This method does not return a value; it prints directly to the standard output.
|
167
|
+
def print_footer(start_hour)
|
168
|
+
timet = "\e]8;;https://github.com/frankvielma/timet/\aTimet\e]8;;\a".green
|
169
|
+
puts '└╴╴╴╴╴╴╴'.gray + timet + "╴╴╴╴╴┴#{'╴' * (24 - start_hour) * 4}╴╴╴┴".gray
|
170
|
+
puts
|
171
|
+
end
|
172
|
+
|
173
|
+
# Prints time blocks for each hour from the start time to 23.
|
174
|
+
#
|
175
|
+
# @param start_time [Integer] The starting hour for printing time blocks.
|
176
|
+
# @param time_block_initial [Hash] A hash containing time block data, where keys are formatted hours and values
|
177
|
+
# are arrays containing block data.
|
178
|
+
# @param colors [Hash] A hash mapping tags to color codes.
|
179
|
+
# @return [void]
|
180
|
+
#
|
181
|
+
# @example
|
182
|
+
# time_block_initial = {
|
183
|
+
# '01' => ['block_char_data', 'tag']
|
184
|
+
# }
|
185
|
+
# colors = { 'tag' => 1 }
|
186
|
+
# print_time_blocks(1, time_block_initial, colors) # Prints time blocks for hours 1 to 23
|
187
|
+
def print_time_blocks(start_time, time_block_initial, colors)
|
188
|
+
(start_time..23).each do |hour|
|
189
|
+
tag, block_char = get_formatted_block_char(hour, time_block_initial)
|
190
|
+
print_colored_block(block_char, tag, colors)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Returns the formatted block character and its associated tag for a given hour.
|
195
|
+
#
|
196
|
+
# @param hour [Integer] The hour for which to retrieve the block character.
|
197
|
+
# @param time_block_initial [Hash] A hash containing time block data, where keys are formatted hours and values
|
198
|
+
# are arrays containing block data.
|
199
|
+
# @return [Array<(String, String)>] An array containing the tag and the block character.
|
200
|
+
#
|
201
|
+
# @example
|
202
|
+
# time_block_initial = {
|
203
|
+
# '01' => ['block_char_data', 'tag']
|
204
|
+
# }
|
205
|
+
# get_formatted_block_char(1, time_block_initial) #=> ['tag', 'block_char']
|
206
|
+
def get_formatted_block_char(hour, time_block_initial)
|
207
|
+
formatted_hour = format('%02d', hour)
|
208
|
+
hour_data = time_block_initial[formatted_hour]
|
209
|
+
tag = hour_data&.last
|
210
|
+
[tag, get_block_char(hour_data&.first)]
|
211
|
+
end
|
212
|
+
|
213
|
+
# Prints a colored block character based on the provided tag and block character.
|
214
|
+
#
|
215
|
+
# @param block_char [String] The block character to be printed.
|
216
|
+
# @param tag [String] The tag associated with the block character, used to determine the color.
|
217
|
+
# @param colors [Hash] A hash mapping tags to color codes.
|
218
|
+
#
|
219
|
+
# @example
|
220
|
+
# colors = { 'tag' => 1 }
|
221
|
+
# print_colored_block('X', 'tag', colors) # Prints a colored block character 'XX'
|
222
|
+
def print_colored_block(block_char, tag, colors)
|
223
|
+
color_code = colors[tag]
|
224
|
+
block = block_char * 2
|
225
|
+
colored_block = color_code ? "#{block.color(color_code + 1)} " : block
|
226
|
+
print colored_block.rjust(4)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Determines the block character based on the value.
|
230
|
+
#
|
231
|
+
# @param value [Integer] The value to determine the block character for.
|
232
|
+
# @return [String] The block character corresponding to the value.
|
233
|
+
def get_block_char(value)
|
234
|
+
return ' ' unless value
|
235
|
+
|
236
|
+
CHAR_MAPPING.find { |range, _| range.include?(value) }&.last || ' '
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|