groupdate 5.0.0 → 5.1.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: fee4526fc30babd6bcffe4121102670b945ac37e5017c47eff03e406a83bf029
4
- data.tar.gz: ce960c7d5c3166f212f93c358ffae736b4306bdedfef6a6db4fe137aea628ade
3
+ metadata.gz: 163a969c3eb174890b78b4512c6a6631e02722a43f1fbcc30926f3e48cdba461
4
+ data.tar.gz: 256ee01a6e991c7513390e8cdf6d27b3e4458018e0980d63e9506bccfc6923d8
5
5
  SHA512:
6
- metadata.gz: fbb0de4e049d1765067cc4c0a175be1e1808b2c56016e54566d00f9b87234cc90fa7ac61d3fbf7683597369d67e2be38dcd08dad9ce20d734224f102d00456be
7
- data.tar.gz: 465694dd5bfb028c1f7f3cd3c9d5fc9ff3dab28de64f394bf765c5f71fdfed67a797ebaf9f24a7a8d3a808e684b296872f58cd71bae088676e3e767e089ab121
6
+ metadata.gz: 37093986846259dc59a17187af24097a2ed9d881de6a48ff9a394883e5a936a569fd758daf820cc9444e69212d29548ab21a816f7fac0cba1b7e502ac2d99198
7
+ data.tar.gz: d1639375454ed0d1d33600226287a484ae7001c3759446b2c196dd24f1e003f3fce7f0c8f2253d90819bc280260bedfed2fa6f622f24ad46c590c05739177df2
@@ -1,3 +1,7 @@
1
+ ## 5.1.0 (2020-07-30)
2
+
3
+ - Added `n` option to minute and second for custom durations
4
+
1
5
  ## 5.0.0 (2020-02-18)
2
6
 
3
7
  - Added support for `week_start` for SQLite
data/README.md CHANGED
@@ -194,6 +194,14 @@ User.group_by_period(params[:period], :created_at, permit: ["day", "week"]).coun
194
194
 
195
195
  Raises an `ArgumentError` for unpermitted periods.
196
196
 
197
+ ### Custom Duration
198
+
199
+ To group by a specific number of minutes or seconds, use:
200
+
201
+ ```ruby
202
+ User.group_by_minute(:created_at, n: 10).count # 10 minutes
203
+ ```
204
+
197
205
  ### Date Columns
198
206
 
199
207
  If grouping on date columns which don’t need time zone conversion, use:
@@ -287,8 +295,6 @@ Groupdate 5.0 brings a number of improvements. Here are a few to be aware of:
287
295
 
288
296
  View the [changelog](https://github.com/ankane/groupdate/blob/master/CHANGELOG.md)
289
297
 
290
- Groupdate follows [Semantic Versioning](https://semver.org/)
291
-
292
298
  ## Contributing
293
299
 
294
300
  Everyone is encouraged to help improve this project. Here are a few ways you can help:
@@ -4,7 +4,7 @@ module Groupdate
4
4
  class Magic
5
5
  DAYS = [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday]
6
6
 
7
- attr_accessor :period, :options, :group_index
7
+ attr_accessor :period, :options, :group_index, :n_seconds
8
8
 
9
9
  def initialize(period:, **options)
10
10
  @period = period
@@ -12,6 +12,13 @@ module Groupdate
12
12
 
13
13
  validate_keywords
14
14
  validate_arguments
15
+
16
+ if options[:n]
17
+ raise ArgumentError, "n must be a positive integer" if !options[:n].is_a?(Integer) || options[:n] < 1
18
+ @period = :custom
19
+ @n_seconds = options[:n].to_i
20
+ @n_seconds *= 60 if period == :minute
21
+ end
15
22
  end
16
23
 
17
24
  def validate_keywords
@@ -28,6 +35,10 @@ module Groupdate
28
35
  @day_start = 0
29
36
  end
30
37
 
38
+ if %i[second minute].include?(period)
39
+ known_keywords << :n
40
+ end
41
+
31
42
  unknown_keywords = options.keys - known_keywords
32
43
  raise ArgumentError, "unknown keywords: #{unknown_keywords.join(", ")}" if unknown_keywords.any?
33
44
  end
@@ -66,7 +77,8 @@ module Groupdate
66
77
  period: period,
67
78
  time_zone: time_zone,
68
79
  day_start: day_start,
69
- week_start: week_start
80
+ week_start: week_start,
81
+ n_seconds: n_seconds
70
82
  )
71
83
  end
72
84
 
@@ -174,7 +186,8 @@ module Groupdate
174
186
  time_zone: magic.time_zone,
175
187
  time_range: magic.time_range,
176
188
  week_start: magic.week_start,
177
- day_start: magic.day_start
189
+ day_start: magic.day_start,
190
+ n_seconds: magic.n_seconds
178
191
  ).generate
179
192
 
180
193
  # add Groupdate info
@@ -1,8 +1,8 @@
1
1
  module Groupdate
2
2
  class RelationBuilder
3
- attr_reader :period, :column, :day_start, :week_start
3
+ attr_reader :period, :column, :day_start, :week_start, :n_seconds
4
4
 
5
- def initialize(relation, column:, period:, time_zone:, time_range:, week_start:, day_start:)
5
+ def initialize(relation, column:, period:, time_zone:, time_range:, week_start:, day_start:, n_seconds:)
6
6
  @relation = relation
7
7
  @column = resolve_column(relation, column)
8
8
  @period = period
@@ -10,6 +10,7 @@ module Groupdate
10
10
  @time_range = time_range
11
11
  @week_start = week_start
12
12
  @day_start = day_start
13
+ @n_seconds = n_seconds
13
14
 
14
15
  if relation.default_timezone == :local
15
16
  raise Groupdate::Error, "ActiveRecord::Base.default_timezone must be :utc to use Groupdate"
@@ -47,6 +48,8 @@ module Groupdate
47
48
  ["CONVERT_TZ(DATE_FORMAT(#{day_start_column} - INTERVAL ((? + DAYOFWEEK(#{day_start_column})) % 7) DAY, '%Y-%m-%d 00:00:00') + INTERVAL ? second, ?, '+00:00')", time_zone, day_start, 12 - week_start, time_zone, day_start, day_start, time_zone]
48
49
  when :quarter
49
50
  ["CONVERT_TZ(DATE_FORMAT(DATE(CONCAT(YEAR(#{day_start_column}), '-', LPAD(1 + 3 * (QUARTER(#{day_start_column}) - 1), 2, '00'), '-01')), '%Y-%m-%d %H:%i:%S') + INTERVAL ? second, ?, '+00:00')", time_zone, day_start, time_zone, day_start, day_start, time_zone]
51
+ when :custom
52
+ ["FROM_UNIXTIME((UNIX_TIMESTAMP(#{column}) DIV ?) * ?)", n_seconds, n_seconds]
50
53
  else
51
54
  format =
52
55
  case period
@@ -85,6 +88,8 @@ module Groupdate
85
88
  ["EXTRACT(MONTH FROM #{day_start_column})::integer", time_zone, day_start_interval]
86
89
  when :week
87
90
  ["(DATE_TRUNC('day', #{day_start_column} - INTERVAL '1 day' * ((? + EXTRACT(DOW FROM #{day_start_column})::integer) % 7)) + INTERVAL ?) AT TIME ZONE ?", time_zone, day_start_interval, 13 - week_start, time_zone, day_start_interval, day_start_interval, time_zone]
91
+ when :custom
92
+ ["TO_TIMESTAMP(FLOOR(EXTRACT(EPOCH FROM #{column}::timestamptz) / ?) * ?)", n_seconds, n_seconds]
88
93
  else
89
94
  if day_start == 0
90
95
  # prettier
@@ -99,6 +104,8 @@ module Groupdate
99
104
 
100
105
  if period == :week
101
106
  ["strftime('%Y-%m-%d 00:00:00 UTC', #{column}, '-6 days', ?)", "weekday #{(week_start + 1) % 7}"]
107
+ elsif period == :custom
108
+ ["datetime((strftime('%s', #{column}) / ?) * ?, 'unixepoch')", n_seconds, n_seconds]
102
109
  else
103
110
  format =
104
111
  case period
@@ -155,6 +162,8 @@ module Groupdate
155
162
  # back to UTC to play properly with the rest of Groupdate.
156
163
  week_start_interval = "#{week_start} day"
157
164
  ["CONVERT_TIMEZONE(?, 'Etc/UTC', DATE_TRUNC('week', #{day_start_column} - INTERVAL ?) + INTERVAL ? + INTERVAL ?)::timestamp", time_zone, time_zone, day_start_interval, week_start_interval, week_start_interval, day_start_interval]
165
+ when :custom
166
+ raise Groupdate::Error, "Not implemented yet"
158
167
  else
159
168
  ["CONVERT_TIMEZONE(?, 'Etc/UTC', DATE_TRUNC(?, #{day_start_column}) + INTERVAL ?)::timestamp", time_zone, period, time_zone, day_start_interval, day_start_interval]
160
169
  end
@@ -1,14 +1,15 @@
1
1
  module Groupdate
2
2
  class SeriesBuilder
3
- attr_reader :period, :time_zone, :day_start, :week_start, :options
3
+ attr_reader :period, :time_zone, :day_start, :week_start, :n_seconds, :options
4
4
 
5
5
  CHECK_PERIODS = [:day, :week, :month, :quarter, :year]
6
6
 
7
- def initialize(period:, time_zone:, day_start:, week_start:, **options)
7
+ def initialize(period:, time_zone:, day_start:, week_start:, n_seconds:, **options)
8
8
  @period = period
9
9
  @time_zone = time_zone
10
10
  @week_start = week_start
11
11
  @day_start = day_start
12
+ @n_seconds = n_seconds
12
13
  @options = options
13
14
  @round_time = {}
14
15
  @week_start_key = Groupdate::Magic::DAYS[@week_start] if @week_start
@@ -73,6 +74,10 @@ module Groupdate
73
74
  end
74
75
 
75
76
  def round_time(time)
77
+ if period == :custom
78
+ return time_zone.at((time.to_time.to_i / n_seconds) * n_seconds)
79
+ end
80
+
76
81
  time = time.to_time.in_time_zone(time_zone)
77
82
 
78
83
  if day_start != 0
@@ -145,6 +150,8 @@ module Groupdate
145
150
  elsif !time_range && options[:last]
146
151
  if period == :quarter
147
152
  step = 3.months
153
+ elsif period == :custom
154
+ step = n_seconds
148
155
  elsif 1.respond_to?(period)
149
156
  step = 1.send(period)
150
157
  else
@@ -216,14 +223,17 @@ module Groupdate
216
223
 
217
224
  if period == :quarter
218
225
  step = 3.months
226
+ elsif period == :custom
227
+ step = n_seconds
219
228
  else
220
229
  step = 1.send(period)
221
230
  end
222
231
 
223
232
  last_step = series.last
233
+ day_start_hour = day_start / 3600
224
234
  loop do
225
235
  next_step = last_step + step
226
- next_step = round_time(next_step) if next_step.hour != day_start # add condition to speed up
236
+ next_step = round_time(next_step) if next_step.hour != day_start_hour # add condition to speed up
227
237
  break unless time_range.cover?(next_step)
228
238
 
229
239
  if next_step == last_step
@@ -1,3 +1,3 @@
1
1
  module Groupdate
2
- VERSION = "5.0.0"
2
+ VERSION = "5.1.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: groupdate
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-19 00:00:00.000000000 Z
11
+ date: 2020-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport