tranzito_utils 1.0.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 +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 [](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: []
|