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: []
         
     |