groupdate 5.0.0 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
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