tranzito_utils 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +137 -0
- data/Rakefile +4 -0
- data/app/views/tranzito_utils/_period_select.html.haml +45 -0
- data/config/locales/en.yml +5 -0
- data/lib/generators/tranzito_utils/install_generator.rb +14 -0
- data/lib/generators/tranzito_utils/templates/tranzito_utils.rb +17 -0
- data/lib/tranzito_utils/concerns/set_period.rb +97 -0
- data/lib/tranzito_utils/concerns/sortable_table.rb +29 -0
- data/lib/tranzito_utils/gem.rb +8 -0
- data/lib/tranzito_utils/helpers/graphing_helper.rb +114 -0
- data/lib/tranzito_utils/helpers/sortable_helper.rb +49 -0
- data/lib/tranzito_utils/services/params_normalizer.rb +10 -0
- data/lib/tranzito_utils/services/time_parser.rb +60 -0
- data/lib/tranzito_utils/version.rb +5 -0
- data/lib/tranzito_utils.rb +21 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c4045a5fec9bb07e94ee83a17aafb7f7e58b1c228de4a5562116a9982a362d12
|
4
|
+
data.tar.gz: dc1d4c81f7f71079c0b66e5b7c6b8e23b88d3fbde4372eef0c50a3ff50569ff6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 501bde401f008a5d0ae611539dec533d448d4d75e40ad23b262a1030818a9785201ff75429435cb4fc78c3b4f59e67b5d41d037d10a8a3a21588bb37d13c4282
|
7
|
+
data.tar.gz: 9ec818c62b80d08cba34103ef490bb6339f431ed835e1d9584428ada2270f4a80bb54e34216577e4cf3ec86e9822b93fb0aae01b5cf7d9783a0e9349ed46c61a
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2022
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
# TranzitoUtils [![CircleCI](https://dl.circleci.com/status-badge/img/gh/Tranzito/tranzito_utils/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/Tranzito/tranzito_utils/tree/main)
|
2
|
+
|
3
|
+
`tranzito_utils` is a ruby gem containing these Rails helpers, concerns and services.
|
4
|
+
|
5
|
+
| Name | Type | Description |
|
6
|
+
| ---- | ---- | ----------- |
|
7
|
+
| `SetPeriod` | Controller Concern | Time period selection and browsing |
|
8
|
+
| `SortableTable` | Controller Concern | Sort column and direction |
|
9
|
+
| `TimeParser` | Service | Parse time strings into activerecord time objects |
|
10
|
+
| `ParamsNormalizer` | Service | Normalize truthy and falsey strings |
|
11
|
+
| `GraphingHelper` | Helper | Graphing helper for [chartkick](https://chartkick.com/) charts |
|
12
|
+
| `SortableHelper` | Helper | Sort table headers |
|
13
|
+
|
14
|
+
# Installation
|
15
|
+
|
16
|
+
## tranzito_utils (gem)
|
17
|
+
Install the gem by adding this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```
|
20
|
+
gem "tranzito_utils"
|
21
|
+
```
|
22
|
+
|
23
|
+
Then run `bundle install`
|
24
|
+
|
25
|
+
To include the config file inside your host app run this command.
|
26
|
+
|
27
|
+
```
|
28
|
+
rails g tranzito_utils:install
|
29
|
+
```
|
30
|
+
|
31
|
+
This will add the `tranzito_utils.rb` file in the initializer where you can alter the default behaviour of several variables being used throughout the gem.
|
32
|
+
|
33
|
+
|
34
|
+
# Usage
|
35
|
+
|
36
|
+
#### SortableTable
|
37
|
+
|
38
|
+
To use the SortableTable module you can include it in your required controllers and then be able to use its methods.
|
39
|
+
|
40
|
+
```
|
41
|
+
include TranzitoUtils::SortableTable
|
42
|
+
```
|
43
|
+
|
44
|
+
You can use the following methods of ` TranzitoUtils::SortableTable` in your controllers. These are also the helper methods.
|
45
|
+
|
46
|
+
```
|
47
|
+
sort_column, sort_direction
|
48
|
+
```
|
49
|
+
|
50
|
+
#### SetPeriod
|
51
|
+
|
52
|
+
Include the `SetPeriod` module into your `ApplicationController` by adding this line
|
53
|
+
|
54
|
+
```
|
55
|
+
include TranzitoUtils::SetPeriod
|
56
|
+
```
|
57
|
+
|
58
|
+
You just need to add this line in the controller where you want to use the `SetPeriod` module.
|
59
|
+
|
60
|
+
```
|
61
|
+
before_action :set_period, only: [:index]
|
62
|
+
```
|
63
|
+
|
64
|
+
Also, the `SetPeriod` has a partial file which you can include in your views using this line.
|
65
|
+
|
66
|
+
```
|
67
|
+
render partial: "/tranzito_utils/period_select"
|
68
|
+
```
|
69
|
+
|
70
|
+
This will include the period_select view for filtering.
|
71
|
+
|
72
|
+
### SortableHelper
|
73
|
+
|
74
|
+
For SortableHelper, you need to add that line into your `application_helper.rb` file.
|
75
|
+
|
76
|
+
```
|
77
|
+
include TranzitoUtils::SortableHelper
|
78
|
+
```
|
79
|
+
|
80
|
+
### GraphingHelper
|
81
|
+
|
82
|
+
Same for GraphingHelper, if you want to use its methods then you need to add that line into your `application_helper.rb` file.
|
83
|
+
|
84
|
+
```
|
85
|
+
include TranzitoUtils::GraphingHelper
|
86
|
+
```
|
87
|
+
|
88
|
+
#### ParamsNormalizer
|
89
|
+
|
90
|
+
Params normalizer uses activerecord to parse strings into booleans.
|
91
|
+
|
92
|
+
## tranzito_utils_js (npm)
|
93
|
+
You also need to add this NPM package in order to use the gem without any issue. You can install it using `yarn` or `npm`.
|
94
|
+
|
95
|
+
For yarn
|
96
|
+
```
|
97
|
+
yarn add tranzito_utils_js
|
98
|
+
```
|
99
|
+
For npm
|
100
|
+
```
|
101
|
+
npm install tranzito_utils_js
|
102
|
+
```
|
103
|
+
|
104
|
+
Then import it into your application by adding this line in `application.js` or any `.js` file where you need it.
|
105
|
+
|
106
|
+
```
|
107
|
+
import { PeriodSelector, TimeParser } from "tranzito_utils_js"
|
108
|
+
```
|
109
|
+
Here `TimeParser` and `PeriodSelector` are classes we can use in our app.
|
110
|
+
|
111
|
+
For `TimeParser` You can use it by initializing like this
|
112
|
+
|
113
|
+
```
|
114
|
+
if (!window.timeParser) { window.timeParser = new TimeParser() }
|
115
|
+
window.timeParser.localize()
|
116
|
+
```
|
117
|
+
|
118
|
+
As for `PeriodSelector`, you can use it by initializing like this
|
119
|
+
```
|
120
|
+
if ($('#timeSelectionBtnGroup').length) {
|
121
|
+
const periodSelector = new PeriodSelector()
|
122
|
+
periodSelector.init()
|
123
|
+
}
|
124
|
+
```
|
125
|
+
`#timeSelectionBtnGroup` is being used in the partial in `tranzito_utils` gem.
|
126
|
+
## License
|
127
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
128
|
+
|
129
|
+
|
130
|
+
## Testing
|
131
|
+
|
132
|
+
To setup testing, you have to create the database:
|
133
|
+
|
134
|
+
```shell
|
135
|
+
cd spec/dummy
|
136
|
+
RAILS_ENV=test bundle exec rails db:create db:schema:load db:migrate
|
137
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
- skip_submission ||= false
|
2
|
+
- include_future ||= false
|
3
|
+
- prepend_text ||= nil
|
4
|
+
|
5
|
+
#timeSelectionBtnGroup.text-right{ role: "group", class: @period == "custom" ? "custom-period-selected" : "", "data-nosubmit" => "#{skip_submission}" }
|
6
|
+
- if prepend_text.present?
|
7
|
+
%span.mr-2.less-strong.d-block.d-lg-inline-block
|
8
|
+
= prepend_text
|
9
|
+
|
10
|
+
- if include_future
|
11
|
+
%a.btn.btn-outline-secondary.btn-sm.period-select-standard{ href: url_for(sortable_search_params.merge(period: "next_week")), class: ("active" if @period == "next_week"), data: { period: "next_week" } }
|
12
|
+
%span.d-none.d-md-inline-block next
|
13
|
+
seven days
|
14
|
+
%a.btn.btn-outline-secondary.btn-sm.period-select-standard{ href: url_for(sortable_search_params.merge(period: "next_month")), class: ("active" if @period == "next_month"), data: { period: "next_month" } }
|
15
|
+
%span.d-none.d-md-inline-block next
|
16
|
+
thirty days
|
17
|
+
%a.btn.btn-outline-secondary.btn-sm.period-select-standard{ href: url_for(sortable_search_params.merge(period: "hour")), class: ("active" if @period == "hour"), data: { period: "hour" } }
|
18
|
+
%span.d-none.d-md-inline-block past
|
19
|
+
hour
|
20
|
+
%a.btn.btn-outline-secondary.btn-sm.period-select-standard{ href: url_for(sortable_search_params.merge(period: "day")), class: ("active" if @period == "day"), data: { period: "day" } }
|
21
|
+
%span.d-none.d-md-inline-block past
|
22
|
+
day
|
23
|
+
%a.btn.btn-outline-secondary.btn-sm.period-select-standard{ href: url_for(sortable_search_params.merge(period: "week")), class: ("active" if @period == "week"), data: { period: "week" } }
|
24
|
+
%span.d-none.d-md-inline-block past
|
25
|
+
seven days
|
26
|
+
%a.btn.btn-outline-secondary.btn-sm.period-select-standard{ href: url_for(sortable_search_params.merge(period: "month")), class: ("active" if @period == "month"), data: { period: "month" } }
|
27
|
+
%span.d-none.d-md-inline-block past
|
28
|
+
thirty days
|
29
|
+
%a.btn.btn-outline-secondary.btn-sm.period-select-standard{ href: url_for(sortable_search_params.merge(period: "year")), class: ("active" if @period == "year"), data: { period: "year" } }
|
30
|
+
%span.d-none.d-md-inline-block past
|
31
|
+
year
|
32
|
+
%a.btn.btn-outline-secondary.btn-sm.period-select-standard{ href: url_for(sortable_search_params.merge(period: "all")), class: ("active" if @period == "all"), data: { period: "all" } }
|
33
|
+
all
|
34
|
+
%button#periodSelectCustom.btn.btn-outline-secondary.btn-sm.ml-2{ class: ("active" if @period == "custom"), data: { period: "custom" } }
|
35
|
+
custom
|
36
|
+
|
37
|
+
%form#timeSelectionCustom.custom-time-selection.mt-2.collapse{ class: @period == "custom" ? "in show" : "" }
|
38
|
+
.form-group
|
39
|
+
= label_tag :start_time_selector, "from", class: "control-label mr-2"
|
40
|
+
= datetime_local_field_tag :start_time_selector, @start_time.strftime("%Y-%m-%dT%H:%M"), step: 60, class: "form-control"
|
41
|
+
.form-group.end-time-control
|
42
|
+
= label_tag :end_time_selector, "to", class: "control-label mr-2 ml-2"
|
43
|
+
= datetime_local_field_tag :end_time_selector, @end_time.strftime("%Y-%m-%dT%H:%M"), step: 60, class: "form-control"
|
44
|
+
%button#updatePeriodSelectCustom.btn.btn-success.btn-sm.ml-2
|
45
|
+
update
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators/base"
|
4
|
+
|
5
|
+
module TranzitoUtils
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
source_root File.expand_path("../templates/", __FILE__)
|
8
|
+
|
9
|
+
# it will copy the tranzito_utils.rb file into the hosts application
|
10
|
+
def copy_initializer
|
11
|
+
template "tranzito_utils.rb", "config/initializers/tranzito_utils.rb"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Tranzito_utils initializer file
|
4
|
+
# Customize only when you want to override the default values of tranzito_utils
|
5
|
+
|
6
|
+
# For TranzitoUtils::SetPeriod
|
7
|
+
# You can update the time for earliest period for SetPeriod concern
|
8
|
+
# TranzitoUtils::DEFAULT[:earliest_period_time] = Time.at(1637388000)
|
9
|
+
|
10
|
+
# For TranzitoUtils::SortableHelper
|
11
|
+
# You can add the more sortable search params for the sortable_search_params method in sortable_helper
|
12
|
+
# TranzitoUtils::DEFAULT[:additional_search_keys] = []
|
13
|
+
|
14
|
+
# For TranzitoUtils::TimeParser service
|
15
|
+
# You can update the earliest year and latest year for TimeParser service
|
16
|
+
# TranzitoUtils::DEFAULT[:earliest_year] = 1900
|
17
|
+
# TranzitoUtils::DEFAULT[:latest_year] = Time.current.year + 100
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TranzitoUtils
|
4
|
+
module SetPeriod
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
PERIOD_TYPES = %w[hour day month year week all next_week next_month].freeze
|
7
|
+
|
8
|
+
# For setting periods, particularly for graphing
|
9
|
+
def set_period
|
10
|
+
set_timezone
|
11
|
+
# Set time period
|
12
|
+
@period ||= params[:period]
|
13
|
+
if @period == "custom"
|
14
|
+
if params[:start_time].present?
|
15
|
+
@start_time = TimeParser.parse(params[:start_time], @timezone)
|
16
|
+
@end_time = TimeParser.parse(params[:end_time], @timezone) || Time.current
|
17
|
+
|
18
|
+
@start_time, @end_time = @end_time, @start_time if @start_time > @end_time
|
19
|
+
else
|
20
|
+
set_time_range_from_period
|
21
|
+
end
|
22
|
+
elsif params[:search_at].present?
|
23
|
+
@period = "custom"
|
24
|
+
@search_at = TimeParser.parse(params[:search_at], @timezone)
|
25
|
+
offset = params[:period].present? ? params[:period].to_i : 10.minutes.to_i
|
26
|
+
@start_time = @search_at - offset
|
27
|
+
@end_time = @search_at + offset
|
28
|
+
else
|
29
|
+
set_time_range_from_period
|
30
|
+
end
|
31
|
+
|
32
|
+
# Add this render_chart in here so we don't have to define it in all the controllers
|
33
|
+
@render_chart = ActiveRecord::Type::Boolean.new.cast(params[:render_chart].to_s.strip)
|
34
|
+
@time_range = @start_time..@end_time
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def set_time_range_from_period
|
40
|
+
@period = default_period unless PERIOD_TYPES.include?(@period)
|
41
|
+
|
42
|
+
case @period
|
43
|
+
when "hour"
|
44
|
+
@start_time = Time.current - 1.hour
|
45
|
+
when "day"
|
46
|
+
@start_time = Time.current.beginning_of_day - 1.day
|
47
|
+
when "month"
|
48
|
+
@start_time = Time.current.beginning_of_day - 30.days
|
49
|
+
when "year"
|
50
|
+
@start_time = Time.current.beginning_of_day - 1.year
|
51
|
+
when "week"
|
52
|
+
@start_time = Time.current.beginning_of_day - 1.week
|
53
|
+
when "next_month"
|
54
|
+
@start_time ||= Time.current
|
55
|
+
@end_time = Time.current.beginning_of_day + 30.days
|
56
|
+
when "next_week"
|
57
|
+
@start_time = Time.current
|
58
|
+
@end_time = Time.current.beginning_of_day + 1.week
|
59
|
+
when "all"
|
60
|
+
@start_time = TranzitoUtils::DEFAULT[:earliest_period_time]
|
61
|
+
else
|
62
|
+
@period = default_period
|
63
|
+
@start_time = TranzitoUtils::DEFAULT[:earliest_period_time]
|
64
|
+
end
|
65
|
+
@end_time ||= latest_period_date
|
66
|
+
end
|
67
|
+
|
68
|
+
# Separate method so it can be overridden on per controller basis
|
69
|
+
def default_period
|
70
|
+
"all"
|
71
|
+
end
|
72
|
+
|
73
|
+
# Separate method so it can be overriden
|
74
|
+
def latest_period_date
|
75
|
+
Time.current
|
76
|
+
end
|
77
|
+
|
78
|
+
def set_timezone
|
79
|
+
return true if @timezone.present?
|
80
|
+
|
81
|
+
# Parse the timezone params if they are passed (tested in admin#activity_groups#index)
|
82
|
+
if params[:timezone].present?
|
83
|
+
@timezone = TimeParser.parse_timezone(params[:timezone])
|
84
|
+
# If it's a valid timezone, save to session
|
85
|
+
session[:timezone] = @timezone&.name
|
86
|
+
end
|
87
|
+
|
88
|
+
# Set the timezone on a per request basis if we have a timezone saved
|
89
|
+
if session[:timezone].present?
|
90
|
+
@timezone ||= TimeParser.parse_timezone(session[:timezone])
|
91
|
+
Time.zone = @timezone
|
92
|
+
end
|
93
|
+
|
94
|
+
@timezone ||= TranzitoUtils::DEFAULT[:time_zone]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TranzitoUtils
|
4
|
+
module SortableTable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
SORT_DIRECTIONS = %w[asc desc].freeze
|
7
|
+
|
8
|
+
included do
|
9
|
+
helper_method :sort_column, :sort_direction
|
10
|
+
end
|
11
|
+
|
12
|
+
def sort_column
|
13
|
+
@sort_column ||= sortable_columns.include?(params[:sort]) ? params[:sort] : default_column
|
14
|
+
end
|
15
|
+
|
16
|
+
def sort_direction
|
17
|
+
@sort_direction ||= SORT_DIRECTIONS.include?(params[:direction]) ? params[:direction] : default_direction
|
18
|
+
end
|
19
|
+
|
20
|
+
# So it can be overridden
|
21
|
+
def default_direction
|
22
|
+
"desc"
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_column
|
26
|
+
sortable_columns.first
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module TranzitoUtils
|
2
|
+
class Gem < Rails::Engine
|
3
|
+
initializer "tranzito_utils.config", before: :load_config_initializers do |app|
|
4
|
+
# Setting the default timezone for timeparser service from host application configuration
|
5
|
+
TranzitoUtils::DEFAULT[:time_zone] = ActiveSupport::TimeZone[Rails.application.config.time_zone]
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TranzitoUtils
|
4
|
+
module GraphingHelper
|
5
|
+
def time_range_counts(collection:, column: "created_at", time_range: nil)
|
6
|
+
time_range ||= @time_range
|
7
|
+
# Note: by specifying the range parameter, we force it to display empty days
|
8
|
+
collection.send(group_by_method(time_range), column, range: time_range, format: group_by_format(time_range))
|
9
|
+
.count
|
10
|
+
end
|
11
|
+
|
12
|
+
def time_range_amounts(collection:, column: "created_at", amount_column: "amount_cents", time_range: nil)
|
13
|
+
time_range ||= @time_range
|
14
|
+
# Note: by specifying the range parameter, we force it to display empty days
|
15
|
+
collection.send(group_by_method(time_range), column, range: time_range, format: group_by_format(time_range))
|
16
|
+
.sum(amount_column)
|
17
|
+
.map { |k, v| [k, (v.to_f / 100.00).round(2)] } # Convert cents to dollars
|
18
|
+
.to_h
|
19
|
+
end
|
20
|
+
|
21
|
+
def time_range_duration(collection:, column: "created_at", duration_column: "duration_seconds", time_range: nil)
|
22
|
+
time_range ||= @time_range
|
23
|
+
# Note: by specifying the range parameter, we force it to display empty days
|
24
|
+
collection.send(group_by_method(time_range), column, range: time_range, format: group_by_format(time_range))
|
25
|
+
.sum(duration_column)
|
26
|
+
.map { |k, v| [k, (v.to_f / 3600.00).round(2)] } # Convert seconds to decimal hours
|
27
|
+
.to_h
|
28
|
+
end
|
29
|
+
|
30
|
+
def group_by_method(time_range)
|
31
|
+
if time_range.last - time_range.first < 3601 # 1.hour + 1 second
|
32
|
+
:group_by_minute
|
33
|
+
elsif time_range.last - time_range.first < 500_000 # around 6 days
|
34
|
+
:group_by_hour
|
35
|
+
elsif time_range.last - time_range.first < 5_000_000 # around 60 days
|
36
|
+
:group_by_day
|
37
|
+
elsif time_range.last - time_range.first < 31449600 # 364 days (52 weeks)
|
38
|
+
:group_by_week
|
39
|
+
else
|
40
|
+
:group_by_month
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def group_by_format(time_range, group_period = nil)
|
45
|
+
group_period ||= group_by_method(time_range)
|
46
|
+
if group_period == :group_by_minute
|
47
|
+
"%l:%M %p"
|
48
|
+
elsif group_period == :group_by_hour
|
49
|
+
"%a%l %p"
|
50
|
+
elsif group_period == :group_by_week
|
51
|
+
"%Y-%-m-%-d"
|
52
|
+
elsif %i[group_by_day group_by_week].include?(group_period) || time_range.present? && time_range.last - time_range.first < 2.weeks.to_i
|
53
|
+
"%a %Y-%-m-%-d"
|
54
|
+
elsif group_period == :group_by_month
|
55
|
+
"%Y-%-m"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def humanized_time_range_column(time_range_column, return_value_for_all: false)
|
60
|
+
return_value_for_all = true if @render_chart
|
61
|
+
return nil unless return_value_for_all || !(@period == "all")
|
62
|
+
humanized_text = time_range_column.to_s.gsub("_at", "").humanize.downcase
|
63
|
+
return humanized_text.gsub("start", "starts") if time_range_column&.match?("start_at")
|
64
|
+
return humanized_text.gsub("end", "ends") if time_range_column&.match?("end_at")
|
65
|
+
return humanized_text.gsub("needs", "need") if time_range_column&.match?("needs_renewal_at")
|
66
|
+
humanized_text
|
67
|
+
end
|
68
|
+
|
69
|
+
def humanized_time_range(time_range)
|
70
|
+
return nil if @period == "all"
|
71
|
+
|
72
|
+
unless @period == "custom"
|
73
|
+
period_display = @period.match?("next_") ? @period.tr("_", " ") : "past #{@period}"
|
74
|
+
return "in the #{period_display}"
|
75
|
+
end
|
76
|
+
|
77
|
+
group_by = group_by_method(time_range)
|
78
|
+
precision_class = if group_by == :group_by_minute
|
79
|
+
"preciseTimeSeconds"
|
80
|
+
elsif group_by == :group_by_hour
|
81
|
+
"preciseTime"
|
82
|
+
else
|
83
|
+
""
|
84
|
+
end
|
85
|
+
|
86
|
+
content_tag(:span) do
|
87
|
+
concat "from "
|
88
|
+
concat content_tag(:em, l(time_range.first, format: :convert_time), class: "convertTime #{precision_class}")
|
89
|
+
concat " to "
|
90
|
+
if time_range.last > Time.current - 5.minutes
|
91
|
+
concat content_tag(:em, "now")
|
92
|
+
else
|
93
|
+
concat content_tag(:em, l(time_range.last, format: :convert_time), class: "convertTime #{precision_class}")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Initially just used by scheduled jobs display, but now used by other things!
|
99
|
+
def period_in_words(seconds)
|
100
|
+
return "" if seconds.blank?
|
101
|
+
|
102
|
+
seconds = seconds.to_i.abs
|
103
|
+
if seconds < 1.minute
|
104
|
+
pluralize(seconds, "second")
|
105
|
+
elsif seconds >= 1.minute && seconds < 1.hour
|
106
|
+
pluralize((seconds / 60.0).round(1), "minute")
|
107
|
+
elsif seconds >= 1.hour && seconds < 24.hours
|
108
|
+
pluralize((seconds / 3600.0).round(1), "hour")
|
109
|
+
elsif seconds >= 24.hours
|
110
|
+
pluralize((seconds / 86400.0).round(1), "day")
|
111
|
+
end.gsub(".0 ", " ") # strip out the empty zero
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TranzitoUtils
|
4
|
+
module SortableHelper
|
5
|
+
DEFAULT_SEARCH_KEYS = [:direction, :sort, :user_id, :period, :start_time, :end_time, :per_page].freeze
|
6
|
+
|
7
|
+
def sortable(column, title = nil, html_options = {})
|
8
|
+
if title.is_a?(Hash) # If title is a hash, it wasn't passed
|
9
|
+
html_options = title
|
10
|
+
title = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
title ||= column.gsub(/_(id|at)\z/, "").titleize
|
14
|
+
# Check for render_sortable - otherwise default to rendering
|
15
|
+
render_sortable = html_options.key?(:render_sortable) ? html_options[:render_sortable] : !html_options[:skip_sortable]
|
16
|
+
return title unless render_sortable
|
17
|
+
|
18
|
+
html_options[:class] = "#{html_options[:class]} sortable-link"
|
19
|
+
direction = column == sort_column && sort_direction == "desc" ? "asc" : "desc"
|
20
|
+
if column == sort_column
|
21
|
+
html_options[:class] += " active"
|
22
|
+
span_content = direction == "asc" ? "\u2193" : "\u2191"
|
23
|
+
end
|
24
|
+
|
25
|
+
link_to(sortable_search_params.merge(sort: column, direction: direction), html_options) do
|
26
|
+
concat(title.html_safe)
|
27
|
+
concat(content_tag(:span, span_content, class: "sortable-direction"))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def sortable_search_params
|
32
|
+
search_param_keys = params.keys.select { |k| k.to_s.start_with?(/search_/) }
|
33
|
+
params.permit(*(DEFAULT_SEARCH_KEYS | TranzitoUtils::DEFAULT[:additional_search_keys] | search_param_keys))
|
34
|
+
end
|
35
|
+
|
36
|
+
def sortable_search_params_without_sort
|
37
|
+
sortable_search_params.except(:direction, :sort)
|
38
|
+
end
|
39
|
+
|
40
|
+
def sortable_search_params?(except: [])
|
41
|
+
except_keys = %i[direction sort period per_page] + except
|
42
|
+
s_params = sortable_search_params.except(*except_keys).values.reject(&:blank?).any?
|
43
|
+
|
44
|
+
return true if s_params
|
45
|
+
return false if except.map(&:to_s).include?("period")
|
46
|
+
params[:period].present? && params[:period] != "all"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TranzitoUtils
|
4
|
+
module TimeParser
|
5
|
+
def self.parse(time_str = nil, timezone_str = nil)
|
6
|
+
return nil unless time_str.present?
|
7
|
+
return time_str if time_str.is_a?(Time)
|
8
|
+
|
9
|
+
if time_str.is_a?(Integer) || time_str.is_a?(Float) || time_str.to_s.strip.match(/^\d+\z/) # it's only numbers
|
10
|
+
return parse("#{time_str}-01-01") if time_str.to_s.length == 4 # Looks a year, valid 8601 format
|
11
|
+
# otherwise it's a timestamp
|
12
|
+
Time.at(time_str.to_i)
|
13
|
+
else
|
14
|
+
Time.zone = parse_timezone(timezone_str)
|
15
|
+
time = Time.zone.parse(time_str.to_s)
|
16
|
+
Time.zone = TranzitoUtils::DEFAULT[:time_zone]
|
17
|
+
time
|
18
|
+
end
|
19
|
+
rescue ArgumentError => e
|
20
|
+
# Try to parse some other, unexpected formats -
|
21
|
+
paychex_formatted = %r{(?<month>\d+)/(?<day>\d+)/(?<year>\d+) (?<hour>\d\d):(?<minute>\d\d) (?<ampm>\w\w)}.match(time_str)
|
22
|
+
ie11_formatted = %r{(?<month>\d+)/(?<day>\d+)/(?<year>\d+)}.match(time_str)
|
23
|
+
just_date = %r{(?<year>\d{4})[^\d|\w](?<month>\d\d?)}.match(time_str)
|
24
|
+
just_date_backward = %r{(?<month>\d\d?)[^\d|\w](?<year>\d{4})}.match(time_str)
|
25
|
+
|
26
|
+
# Get the successful matching regex group, and then reformat it in an expected way
|
27
|
+
regex_match = [paychex_formatted, ie11_formatted, just_date, just_date_backward].compact.first
|
28
|
+
raise e unless regex_match.present?
|
29
|
+
|
30
|
+
new_str = %w[year month day]
|
31
|
+
.map { |component| regex_match[component] if regex_match.names.include?(component) }
|
32
|
+
.compact
|
33
|
+
.join("-")
|
34
|
+
|
35
|
+
# If we end up with an unreasonable year, throw an error
|
36
|
+
raise e unless new_str.split("-").first.to_i.between?(TranzitoUtils::DEFAULT[:earliest_year], TranzitoUtils::DEFAULT[:latest_year])
|
37
|
+
# Add the day, if there isn't one
|
38
|
+
new_str += "-01" unless regex_match.names.include?("day")
|
39
|
+
# If it's paychex_formatted there is an hour and minute
|
40
|
+
if paychex_formatted.present?
|
41
|
+
new_str += " #{regex_match["hour"]}:#{regex_match["minute"]}#{regex_match["ampm"]}"
|
42
|
+
end
|
43
|
+
# Run it through TimeParser again
|
44
|
+
parse(new_str, timezone_str)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.parse_timezone(timezone_str)
|
48
|
+
return TranzitoUtils::DEFAULT[:time_zone] unless timezone_str.present?
|
49
|
+
return timezone_str if timezone_str.is_a?(ActiveSupport::TimeZone) # in case we were given a timezone obj
|
50
|
+
|
51
|
+
# tzinfo requires non-whitespaced strings, so try that if the normal lookup fails
|
52
|
+
ActiveSupport::TimeZone[timezone_str] || ActiveSupport::TimeZone[timezone_str.strip.tr("\s", "_")]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Accepts a time object, rounds to minutes
|
56
|
+
def self.round(time, unit = "minute")
|
57
|
+
unit == "second" ? time.change(usec: 0, sec: 0) : time.change(min: 0, usec: 0, sec: 0)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tranzito_utils/version"
|
4
|
+
|
5
|
+
module TranzitoUtils
|
6
|
+
DEFAULT = {
|
7
|
+
earliest_period_time: Time.at(1637388000),
|
8
|
+
earliest_year: 1900,
|
9
|
+
latest_year: (Time.current.year + 100),
|
10
|
+
additional_search_keys: [],
|
11
|
+
time_zone: ""
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
require "tranzito_utils/concerns/set_period"
|
16
|
+
require "tranzito_utils/concerns/sortable_table"
|
17
|
+
require "tranzito_utils/helpers/graphing_helper"
|
18
|
+
require "tranzito_utils/helpers/sortable_helper"
|
19
|
+
require "tranzito_utils/services/time_parser"
|
20
|
+
require "tranzito_utils/services/params_normalizer"
|
21
|
+
require "tranzito_utils/gem"
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tranzito_utils
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- willbarrett
|
8
|
+
- sethherr
|
9
|
+
- hafiz-ahmed
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2022-09-26 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rails
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ">="
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '6.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '6.0'
|
29
|
+
description: Ruby gem contain several modules mainly containing the helpers, concerns
|
30
|
+
and services for personal use by Tranzito
|
31
|
+
email:
|
32
|
+
- info@tranzito.org
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- MIT-LICENSE
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- app/views/tranzito_utils/_period_select.html.haml
|
41
|
+
- config/locales/en.yml
|
42
|
+
- lib/generators/tranzito_utils/install_generator.rb
|
43
|
+
- lib/generators/tranzito_utils/templates/tranzito_utils.rb
|
44
|
+
- lib/tranzito_utils.rb
|
45
|
+
- lib/tranzito_utils/concerns/set_period.rb
|
46
|
+
- lib/tranzito_utils/concerns/sortable_table.rb
|
47
|
+
- lib/tranzito_utils/gem.rb
|
48
|
+
- lib/tranzito_utils/helpers/graphing_helper.rb
|
49
|
+
- lib/tranzito_utils/helpers/sortable_helper.rb
|
50
|
+
- lib/tranzito_utils/services/params_normalizer.rb
|
51
|
+
- lib/tranzito_utils/services/time_parser.rb
|
52
|
+
- lib/tranzito_utils/version.rb
|
53
|
+
homepage: https://github.com/Tranzito/tranzito_utils
|
54
|
+
licenses:
|
55
|
+
- MIT
|
56
|
+
metadata:
|
57
|
+
homepage_uri: https://github.com/Tranzito/tranzito_utils
|
58
|
+
source_code_uri: https://github.com/Tranzito/tranzito_utils
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubygems_version: 3.1.2
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: Ruby gem contain several modules mainly containing the helpers, concerns
|
78
|
+
and services for personal use by Tranzito
|
79
|
+
test_files: []
|