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 +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +8 -2
- data/lib/groupdate/magic.rb +16 -3
- data/lib/groupdate/relation_builder.rb +11 -2
- data/lib/groupdate/series_builder.rb +13 -3
- data/lib/groupdate/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 163a969c3eb174890b78b4512c6a6631e02722a43f1fbcc30926f3e48cdba461
|
4
|
+
data.tar.gz: 256ee01a6e991c7513390e8cdf6d27b3e4458018e0980d63e9506bccfc6923d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37093986846259dc59a17187af24097a2ed9d881de6a48ff9a394883e5a936a569fd758daf820cc9444e69212d29548ab21a816f7fac0cba1b7e502ac2d99198
|
7
|
+
data.tar.gz: d1639375454ed0d1d33600226287a484ae7001c3759446b2c196dd24f1e003f3fce7f0c8f2253d90819bc280260bedfed2fa6f622f24ad46c590c05739177df2
|
data/CHANGELOG.md
CHANGED
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:
|
data/lib/groupdate/magic.rb
CHANGED
@@ -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 !=
|
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
|
data/lib/groupdate/version.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2020-07-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|