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 +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
|