flycal-cli 0.2.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c7e777c9b4698b273770ea0d9a70edf232ffe70f2e6bd9d32ab65818ca0f5cb5
4
- data.tar.gz: 5d62d5fede48b31930a6a652c8b4f165a0f82315eb85e3fb5be885e56ffc2ae7
3
+ metadata.gz: c6e56787081596ebef227a579b0667514fa493998875f613167acdade53a4899
4
+ data.tar.gz: a8776aded68f58ba58759adc27b3aeb2ca63eacf9797eb8bc161aeb2f96589ee
5
5
  SHA512:
6
- metadata.gz: c1d95fcb97e10fa0c1144e15c7e205cb693d2c48847dc96569cb718c8899c5ecb3479504b4c19b3315dd8c74ecbcbc9510e99f2070a4c392ae206c51dba395dd
7
- data.tar.gz: 6d9d08601b3d78d1aa8d3520b2f0102517b80be6ac708d0d036e3e5558d2548c1a57613f9b3121ebb12329f564a682b71843ac96b5c56a07cdf578f4dcab8f71
6
+ metadata.gz: 0e0af4be4d8505aa21a63e4e6ed70f886419eddf971745154174ea55ec6348030cc0fdad3e40af926df128fa295a5741da84fe236930b2ec797259c2ba300219
7
+ data.tar.gz: d2021d42bc26dd9b82c4b7b672c923b137b2e83e1f983af96310b9c9d14e948c33cce752f99ba1f99e175d9071067484d2eb5b0763b5d8d63bf1ccb0d56085f4
data/README.md CHANGED
@@ -101,6 +101,8 @@ The summary shows:
101
101
  - Number of events found
102
102
  - Total time occupied (hours, minutes, and working days based on 8-hour days)
103
103
 
104
+ For time frames longer than 7 days, a weekly breakdown is added (week number, start/end dates, hours, working days per week). For time frames longer than 30 days, a monthly breakdown is shown instead (month number, month name, hours, working days per month).
105
+
104
106
  ## Configuration
105
107
 
106
108
  Data is stored in `~/.flycal/`:
@@ -3,6 +3,9 @@
3
3
  require "thor"
4
4
  require "time"
5
5
  require "date"
6
+ require "active_support/core_ext/date"
7
+ require "active_support/core_ext/integer"
8
+ require "active_support/core_ext/time"
6
9
  require "tty-prompt"
7
10
  require "tty-spinner"
8
11
 
@@ -130,10 +133,10 @@ module FlycalCli
130
133
  time_min = options[:from].to_s.empty? ? Date.today.to_time : parse_datetime(options[:from])
131
134
  begin
132
135
  time_max = if options[:in].to_s.empty?
133
- options[:to].to_s.empty? ? (Date.today + 30).to_time + 86400 - 1 : parse_datetime(options[:to], end_of_day: true)
134
- else
135
- parse_duration_in(options[:in], time_min)
136
- end
136
+ options[:to].to_s.empty? ? (Date.today + 30).to_time + 86400 - 1 : parse_datetime(options[:to], end_of_day: true)
137
+ else
138
+ parse_duration_in(options[:in], time_min)
139
+ end
137
140
  rescue FlycalCli::Error => e
138
141
  puts "Error: #{e.message}"
139
142
  exit 1
@@ -161,13 +164,19 @@ module FlycalCli
161
164
  )
162
165
 
163
166
  print_events(service, events)
164
- print_summary(events, time_min: time_min, time_max: time_max)
167
+ print_search_summary(events, time_min: time_min, time_max: time_max)
165
168
  end
166
169
 
167
170
  default_task :help
168
171
 
169
172
  private
170
173
 
174
+ HOURS_PER_WORKING_DAY = 8
175
+
176
+ def bold(str)
177
+ "\e[1m#{str}\e[0m"
178
+ end
179
+
171
180
  def parse_datetime(str, end_of_day: false)
172
181
  return nil if str.nil? || str.empty?
173
182
 
@@ -255,36 +264,112 @@ module FlycalCli
255
264
  if dt.is_a?(String)
256
265
  dt
257
266
  else
258
- dt.strftime("%Y-%m-%d %H:%M")
267
+ dt.strftime("%a %Y-%m-%d %H:%M")
259
268
  end
260
269
  end
261
270
 
262
- def print_summary(events, time_min:, time_max:)
263
- total_minutes = 0
271
+ def format_date_with_day(dt)
272
+ return "-" if dt.nil?
264
273
 
265
- events.each do |item|
266
- event = item[:event]
267
- start = event.start&.date_time || event.start&.date
268
- end_dt = event.end&.date_time || event.end&.date
274
+ t = dt.respond_to?(:to_time) ? dt.to_time : dt
275
+ t.strftime("%a %Y-%m-%d")
276
+ end
269
277
 
270
- next if start.nil? || end_dt.nil?
278
+ def print_search_summary(events, time_min:, time_max:)
279
+ event_ranges = extract_event_ranges(events)
280
+ total_minutes = event_ranges.sum { |s, e| (e - s) / 60 }
271
281
 
272
- start = start.to_time if start.respond_to?(:to_time)
273
- end_dt = end_dt.to_time if end_dt.respond_to?(:to_time)
274
- total_minutes += (end_dt - start) / 60
282
+ from_str = time_min.strftime("%a %Y-%m-%d %H:%M")
283
+ to_str = time_max.strftime("%a %Y-%m-%d %H:%M")
284
+
285
+ puts "\n---"
286
+ puts "From: #{from_str} | To: #{to_str}"
287
+ puts "Events found: #{events.size}"
288
+ puts "Total time occupied: #{format_duration(total_minutes)}"
289
+
290
+ frame_days = (time_max - time_min) / 86400.0
291
+ if frame_days > 30
292
+ print_monthly_breakdown(event_ranges, time_min, time_max)
293
+ elsif frame_days > 7
294
+ print_weekly_breakdown(event_ranges, time_min, time_max)
275
295
  end
296
+ end
297
+
298
+ def extract_event_ranges(events)
299
+ events.filter_map do |item|
300
+ event = item[:event]
301
+ start_t = event.start&.date_time || event.start&.date
302
+ end_t = event.end&.date_time || event.end&.date
303
+ next if start_t.nil? || end_t.nil?
276
304
 
305
+ start_t = start_t.to_time if start_t.respond_to?(:to_time)
306
+ end_t = end_t.to_time if end_t.respond_to?(:to_time)
307
+ [start_t, end_t]
308
+ end
309
+ end
310
+
311
+ def format_duration(total_minutes)
277
312
  hours = (total_minutes / 60).floor
278
313
  mins = (total_minutes % 60).round
279
- total_working_days = (total_minutes / 60.0 / 8).round(1)
314
+ working_days = (total_minutes / 60.0 / HOURS_PER_WORKING_DAY).round(1)
315
+ "#{bold(hours)}h #{bold(mins)}min (#{bold(working_days)} working days)"
316
+ end
280
317
 
281
- from_str = time_min.strftime("%Y-%m-%d %H:%M")
282
- to_str = time_max.strftime("%Y-%m-%d %H:%M")
318
+ def format_hours_and_days(hours, working_days)
319
+ "#{bold(hours)}h (#{bold(working_days)} working days)"
320
+ end
283
321
 
284
- puts "\n---"
285
- puts "From: #{from_str} | To: #{to_str}"
286
- puts "Events found: #{events.size}"
287
- puts "Total time occupied: #{hours}h #{mins}min (#{total_working_days} working days)"
322
+ def minutes_in_period(event_ranges, period_start, period_end)
323
+ event_ranges.sum do |ev_start, ev_end|
324
+ overlap_start = [ev_start, period_start].max
325
+ overlap_end = [ev_end, period_end].min
326
+ overlap_sec = overlap_end - overlap_start
327
+ overlap_sec > 0 ? overlap_sec / 60.0 : 0
328
+ end
329
+ end
330
+
331
+ def print_weekly_breakdown(event_ranges, time_min, time_max)
332
+ puts "\nBy week:"
333
+ week_num = 1
334
+ current = time_min.to_date.beginning_of_week(:monday)
335
+ while current.to_time < time_max
336
+ week_start = [current.to_time, time_min].max
337
+ week_end_date = current.end_of_week(:monday)
338
+ week_end = [week_end_date.to_time + 1.day, time_max].min
339
+ mins = minutes_in_period(event_ranges, week_start, week_end)
340
+ hours = (mins / 60).round(1)
341
+ working_days = (mins / 60.0 / HOURS_PER_WORKING_DAY).round(1)
342
+ start_str = format_date_with_day(week_start)
343
+ end_str = format_date_with_day(week_end_date)
344
+ puts " #{bold(week_num)}. #{start_str} - #{end_str}: #{format_hours_and_days(hours, working_days)}"
345
+ week_num += 1
346
+ current = current + 7.days
347
+ end
348
+ end
349
+
350
+ def print_monthly_breakdown(event_ranges, time_min, time_max)
351
+ puts "\nBy month:"
352
+ month_num = 1
353
+ current = time_min.to_date.beginning_of_month
354
+ end_date = time_max.to_date
355
+
356
+ while current <= end_date
357
+ month_start = current.beginning_of_month.to_time
358
+ month_end = (current.end_of_month + 1.day).to_time
359
+ period_start = [month_start, time_min].max
360
+ period_end = [month_end, time_max].min
361
+
362
+ mins = minutes_in_period(event_ranges, period_start, period_end)
363
+ hours = (mins / 60).round(1)
364
+ working_days = (mins / 60.0 / HOURS_PER_WORKING_DAY).round(1)
365
+ month_name = current.strftime("%B %Y")
366
+ start_str = format_date_with_day(period_start)
367
+ last_day = (period_end.to_date - 1.day)
368
+ end_str = format_date_with_day(last_day)
369
+ puts " #{month_num}. #{month_name} (#{start_str} - #{end_str}): #{format_hours_and_days(hours, working_days)}"
370
+ month_num += 1
371
+ current = current.next_month.beginning_of_month
372
+ end
288
373
  end
289
374
  end
290
375
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FlycalCli
4
- VERSION = '0.2.1'
4
+ VERSION = '0.3.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flycal-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - flycal-cli
@@ -79,6 +79,20 @@ dependencies:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
81
  version: '0.9'
82
+ - !ruby/object:Gem::Dependency
83
+ name: activesupport
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '6.0'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '6.0'
82
96
  - !ruby/object:Gem::Dependency
83
97
  name: webrick
84
98
  requirement: !ruby/object:Gem::Requirement