From 85ea00fe0b850cf7c19be96bd613be91036fa58d Mon Sep 17 00:00:00 2001 From: Denis Talakevich Date: Wed, 31 Jul 2019 21:30:37 +0300 Subject: [PATCH 1/2] Admin UI fix custom data totals --- app/admin/reports/custom_data.rb | 2 +- app/models/account.rb | 5 +++- app/models/billing/invoice.rb | 9 +++++- .../concerns/acts_as_totals_relation.rb | 10 +++++++ app/models/report/custom_data.rb | 17 +++++++---- .../customer_traffic_data_by_destination.rb | 28 ++++++++++++------- .../report/customer_traffic_data_by_vendor.rb | 28 ++++++++++++------- .../report/customer_traffic_data_full.rb | 28 ++++++++++++------- app/models/report/vendor_traffic_data.rb | 26 +++++++++++------ 9 files changed, 105 insertions(+), 48 deletions(-) create mode 100644 app/models/concerns/acts_as_totals_relation.rb diff --git a/app/admin/reports/custom_data.rb b/app/admin/reports/custom_data.rb index b6622e691..fc76f67f0 100644 --- a/app/admin/reports/custom_data.rb +++ b/app/admin/reports/custom_data.rb @@ -64,7 +64,7 @@ def scoped_collection column :calls_count, sortable: :agg_calls_count, footer: lambda { strong do - text_node @footer_data[:agg_calls_count].to_s + text_node @footer_data.agg_calls_count.to_s text_node ' calls' end }, &:agg_calls_count diff --git a/app/models/account.rb b/app/models/account.rb index aa3181087..ff3d928bf 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -89,8 +89,11 @@ def send_balance_notifications_to_emails contacts_for_balance_notifications.map(&:email).join(',') end + Totals = Struct.new(:total_balance) + def self.totals - except(:eager_load).select('sum(balance) as total_balance').take + row = extending(ActsAsTotalsRelation).totals_row_by('sum(balance) as total_balance') + Totals.new(*row) end def contacts_for_invoices diff --git a/app/models/billing/invoice.rb b/app/models/billing/invoice.rb index e4a3f9253..d9beb1240 100644 --- a/app/models/billing/invoice.rb +++ b/app/models/billing/invoice.rb @@ -302,8 +302,15 @@ def file_name "#{id}_#{start_date}_#{end_date}" end + Totals = Struct.new(:total_amount, :total_calls_count, :total_calls_duration) + def self.totals - except(:eager_load).select('sum(amount) as total_amount, sum(calls_count) as total_calls_count, sum(calls_duration) as total_calls_duration').take + row = extending(ActsAsTotalsRelation).totals_row_by( + 'sum(amount) as total_amount', + 'sum(calls_count) as total_calls_count', + 'sum(calls_duration) as total_calls_duration' + ) + Totals.new(*row) end def contacts_for_invoices diff --git a/app/models/concerns/acts_as_totals_relation.rb b/app/models/concerns/acts_as_totals_relation.rb new file mode 100644 index 000000000..db04b5644 --- /dev/null +++ b/app/models/concerns/acts_as_totals_relation.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module ActsAsTotalsRelation + def totals_row_by(*select_sql) + safe_select_sql = select_sql.map { |sql| Arel.sql(sql) } + except(:preload, :includes, :eager_load, :limit, :offset, :select, :order) + .pluck(safe_select_sql) + .first + end +end diff --git a/app/models/report/custom_data.rb b/app/models/report/custom_data.rb index d227446d1..df5e4d9f6 100644 --- a/app/models/report/custom_data.rb +++ b/app/models/report/custom_data.rb @@ -120,12 +120,17 @@ def self.report_columns column_names.select { |column| column.start_with?('agg_') } end + Totals = Struct.new(:agg_calls_count, :agg_calls_duration, :agg_acd, :agg_customer_price, :agg_vendor_price, :agg_profit) + def self.totals - select("sum(agg_calls_count)::integer as agg_calls_count, - sum(agg_calls_duration) as agg_calls_duration, - coalesce(sum(agg_calls_duration)::float/nullif(sum(agg_calls_count),0),0) as agg_acd, - sum(agg_customer_price) as agg_customer_price, - sum(agg_vendor_price) as agg_vendor_price, - sum(agg_profit) as agg_profit").take + row = extending(ActsAsTotalsRelation).totals_row_by( + 'sum(agg_calls_count)::integer as agg_calls_count', + 'sum(agg_calls_duration) as agg_calls_duration', + 'coalesce(sum(agg_calls_duration)::float/nullif(sum(agg_calls_count),0),0) as agg_acd', + 'sum(agg_customer_price) as agg_customer_price', + 'sum(agg_vendor_price) as agg_vendor_price', + 'sum(agg_profit) as agg_profit' + ) + Totals.new(*row) end end diff --git a/app/models/report/customer_traffic_data_by_destination.rb b/app/models/report/customer_traffic_data_by_destination.rb index a68aa4fdb..6949dcc9a 100644 --- a/app/models/report/customer_traffic_data_by_destination.rb +++ b/app/models/report/customer_traffic_data_by_destination.rb @@ -33,17 +33,25 @@ def display_name id.to_s end + Totals = Struct.new( + :calls_count, :success_calls_count, :short_calls_count, :calls_duration, :origination_cost, + :termination_cost, :profit, :first_call_at, :last_call_at, :agg_acd + ) + def self.totals - select("sum(calls_count)::int as calls_count, - sum(success_calls_count)::int as success_calls_count, - sum(short_calls_count)::int as short_calls_count, - sum(calls_duration) as calls_duration, - sum(origination_cost) as origination_cost, - sum(termination_cost) as termination_cost, - sum(profit) as profit, - min(first_call_at) as first_call_at, - max(last_call_at) as last_call_at, - coalesce(sum(calls_duration)::float/nullif(sum(success_calls_count),0),0) as agg_acd").take + row = extending(ActsAsTotalsRelation).totals_row_by( + 'sum(calls_count)::int as calls_count', + 'sum(success_calls_count)::int as success_calls_count', + 'sum(short_calls_count)::int as short_calls_count', + 'sum(calls_duration) as calls_duration', + 'sum(origination_cost) as origination_cost', + 'sum(termination_cost) as termination_cost', + 'sum(profit) as profit', + 'min(first_call_at) as first_call_at', + 'max(last_call_at) as last_call_at', + 'coalesce(sum(calls_duration)::float/nullif(sum(success_calls_count),0),0) as agg_acd' + ) + Totals.new(*row) end def self.report_records diff --git a/app/models/report/customer_traffic_data_by_vendor.rb b/app/models/report/customer_traffic_data_by_vendor.rb index 15ab34eb4..ae9348cf2 100644 --- a/app/models/report/customer_traffic_data_by_vendor.rb +++ b/app/models/report/customer_traffic_data_by_vendor.rb @@ -30,16 +30,24 @@ def display_name id.to_s end + Totals = Struct.new( + :calls_count, :success_calls_count, :short_calls_count, :calls_duration, :origination_cost, + :termination_cost, :profit, :first_call_at, :last_call_at, :agg_acd + ) + def self.totals - select("sum(calls_count)::int as calls_count, - sum(success_calls_count)::int as success_calls_count, - sum(short_calls_count)::int as short_calls_count, - sum(calls_duration) as calls_duration, - sum(origination_cost) as origination_cost, - sum(termination_cost) as termination_cost, - sum(profit) as profit, - min(first_call_at) as first_call_at, - max(last_call_at) as last_call_at, - coalesce(sum(calls_duration)::float/nullif(sum(success_calls_count),0),0) as agg_acd").take + row = extending(ActsAsTotalsRelation).totals_row_by( + 'sum(calls_count)::int as calls_count', + 'sum(success_calls_count)::int as success_calls_count', + 'sum(short_calls_count)::int as short_calls_count', + 'sum(calls_duration) as calls_duration', + 'sum(origination_cost) as origination_cost', + 'sum(termination_cost) as termination_cost', + 'sum(profit) as profit', + 'min(first_call_at) as first_call_at', + 'max(last_call_at) as last_call_at', + 'coalesce(sum(calls_duration)::float/nullif(sum(success_calls_count),0),0) as agg_acd' + ) + Totals.new(*row) end end diff --git a/app/models/report/customer_traffic_data_full.rb b/app/models/report/customer_traffic_data_full.rb index c20ab5616..5e5f4bc06 100644 --- a/app/models/report/customer_traffic_data_full.rb +++ b/app/models/report/customer_traffic_data_full.rb @@ -35,16 +35,24 @@ def display_name id.to_s end + Totals = Struct.new( + :calls_count, :success_calls_count, :short_calls_count, :calls_duration, :origination_cost, + :termination_cost, :profit, :first_call_at, :last_call_at, :agg_acd + ) + def self.totals - select("sum(calls_count)::int as calls_count, - sum(success_calls_count)::int as success_calls_count, - sum(short_calls_count)::int as short_calls_count, - sum(calls_duration) as calls_duration, - sum(origination_cost) as origination_cost, - sum(termination_cost) as termination_cost, - sum(profit) as profit, - min(first_call_at) as first_call_at, - max(last_call_at) as last_call_at, - coalesce(sum(calls_duration)::float/nullif(sum(success_calls_count),0),0) as agg_acd").take + row = extending(ActsAsTotalsRelation).totals_row_by( + 'sum(calls_count)::int as calls_count', + 'sum(success_calls_count)::int as success_calls_count', + 'sum(short_calls_count)::int as short_calls_count', + 'sum(calls_duration) as calls_duration', + 'sum(origination_cost) as origination_cost', + 'sum(termination_cost) as termination_cost', + 'sum(profit) as profit', + 'min(first_call_at) as first_call_at', + 'max(last_call_at) as last_call_at', + 'coalesce(sum(calls_duration)::float/nullif(sum(success_calls_count),0),0) as agg_acd' + ) + Totals.new(*row) end end diff --git a/app/models/report/vendor_traffic_data.rb b/app/models/report/vendor_traffic_data.rb index 17beb5f2b..d2d76e51d 100644 --- a/app/models/report/vendor_traffic_data.rb +++ b/app/models/report/vendor_traffic_data.rb @@ -30,15 +30,23 @@ def display_name id.to_s end + Totals = Struct.new( + :calls_count, :success_calls_count, :short_calls_count, :calls_duration, :origination_cost, + :termination_cost, :profit, :first_call_at, :last_call_at + ) + def self.totals - select("sum(calls_count)::int as calls_count, - sum(success_calls_count)::int as success_calls_count, - sum(short_calls_count)::int as short_calls_count, - sum(calls_duration) as calls_duration, - sum(origination_cost) as origination_cost, - sum(termination_cost) as termination_cost, - sum(profit) as profit, - min(first_call_at) as first_call_at, - max(last_call_at) as last_call_at").to_a.first + row = extending(ActsAsTotalsRelation).totals_row_by( + 'sum(calls_count)::int as calls_count', + 'sum(success_calls_count)::int as success_calls_count', + 'sum(short_calls_count)::int as short_calls_count', + 'sum(calls_duration) as calls_duration', + 'sum(origination_cost) as origination_cost', + 'sum(termination_cost) as termination_cost', + 'sum(profit) as profit', + 'min(first_call_at) as first_call_at', + 'max(last_call_at) as last_call_at' + ) + Totals.new(*row) end end From d1a41bf8f41899b8f6514da443be49d920bb0760 Mon Sep 17 00:00:00 2001 From: Denis Talakevich Date: Wed, 31 Jul 2019 21:30:58 +0300 Subject: [PATCH 2/2] Admin UI auth logs add default filter --- app/admin/cdr/auth_logs.rb | 5 ++++ app/admin/logs/api_log.rb | 8 ++---- app/admin/reports/realtime/bad_routing.rb | 8 +----- .../reports/realtime/not_authenticated.rb | 8 +----- .../realtime/origination_performance.rb | 8 +----- .../realtime/termination_distribution.rb | 8 +----- config/initializers/yeti.rb | 1 + lib/resource_dsl/with_default_params.rb | 28 +++++++++++++++++++ 8 files changed, 41 insertions(+), 33 deletions(-) create mode 100644 lib/resource_dsl/with_default_params.rb diff --git a/app/admin/cdr/auth_logs.rb b/app/admin/cdr/auth_logs.rb index 1c4f3b580..233a6314f 100644 --- a/app/admin/cdr/auth_logs.rb +++ b/app/admin/cdr/auth_logs.rb @@ -7,6 +7,11 @@ config.batch_actions = false config.sort_order = 'request_time_desc' + with_default_params do + params[:q] = { request_time_gteq_datetime: 1.days.ago.to_date.strftime('%F') } + 'Only records from yesterday are displayed by default' + end + acts_as_export :id, :request_time, [:gateway_name, proc { |row| row.gateway.try(:name) }], diff --git a/app/admin/logs/api_log.rb b/app/admin/logs/api_log.rb index 066ee88d5..06189d2ae 100644 --- a/app/admin/logs/api_log.rb +++ b/app/admin/logs/api_log.rb @@ -9,11 +9,9 @@ scope :all, default: true scope :failed, show_count: false - before_action only: [:index] do - if params['q'].blank? - params['q'] = { created_at_gteq: 1.days.ago } # only 1 last days by default - flash.now[:notice] = 'Only records for last day are displayed by default' - end + with_default_params do + params[:q] = { created_at_gteq: 1.days.ago } # only 1 last days by default + 'Only records for last day are displayed by default' end controller do diff --git a/app/admin/reports/realtime/bad_routing.rb b/app/admin/reports/realtime/bad_routing.rb index bff133280..1df6ee38f 100644 --- a/app/admin/reports/realtime/bad_routing.rb +++ b/app/admin/reports/realtime/bad_routing.rb @@ -22,13 +22,7 @@ filter :internal_disconnect_code filter :internal_disconnect_reason - before_action only: [:index] do - params[:q] ||= {} - if params[:q][:time_interval_eq].blank? - params[:q][:time_interval_eq] = Report::Realtime::Base::DEFAULT_INTERVAL - flash.now[:notice_message] = "Records for time interval #{Report::Realtime::Base::DEFAULT_INTERVAL} seconds are displayed by default" - end - end + with_default_realtime_interval controller do def scoped_collection diff --git a/app/admin/reports/realtime/not_authenticated.rb b/app/admin/reports/realtime/not_authenticated.rb index f291c53df..cf27422d8 100644 --- a/app/admin/reports/realtime/not_authenticated.rb +++ b/app/admin/reports/realtime/not_authenticated.rb @@ -12,13 +12,7 @@ collection: Report::Realtime::Base::INTERVALS, input_html: { class: 'chosen' }, include_blank: false - before_action only: [:index] do - params[:q] ||= {} - if params[:q][:time_interval_eq].blank? - params[:q][:time_interval_eq] = Report::Realtime::Base::DEFAULT_INTERVAL - flash.now[:notice_message] = "Records for time interval #{Report::Realtime::Base::DEFAULT_INTERVAL} seconds are displayed by default" - end - end + with_default_realtime_interval controller do def scoped_collection diff --git a/app/admin/reports/realtime/origination_performance.rb b/app/admin/reports/realtime/origination_performance.rb index c7b839afa..8f594d36e 100644 --- a/app/admin/reports/realtime/origination_performance.rb +++ b/app/admin/reports/realtime/origination_performance.rb @@ -19,13 +19,7 @@ collection: proc { Contractor.select(:id, :name).reorder(:name) }, input_html: { class: 'chosen' } - before_action only: [:index] do - params[:q] ||= {} - if params[:q][:time_interval_eq].blank? - params[:q][:time_interval_eq] = Report::Realtime::Base::DEFAULT_INTERVAL - flash.now[:notice_message] = "Records for time interval #{Report::Realtime::Base::DEFAULT_INTERVAL} seconds are displayed by default" - end - end + with_default_realtime_interval controller do def scoped_collection diff --git a/app/admin/reports/realtime/termination_distribution.rb b/app/admin/reports/realtime/termination_distribution.rb index 8c3733b15..aa31731f5 100644 --- a/app/admin/reports/realtime/termination_distribution.rb +++ b/app/admin/reports/realtime/termination_distribution.rb @@ -18,13 +18,7 @@ as: :select, collection: proc { Contractor.select(:id, :name).reorder(:name) }, input_html: { class: 'chosen' } - before_action only: [:index] do - params[:q] ||= {} - if params[:q][:time_interval_eq].blank? - params[:q][:time_interval_eq] = Report::Realtime::Base::DEFAULT_INTERVAL - flash.now[:notice_message] = "Records for time interval #{Report::Realtime::Base::DEFAULT_INTERVAL} seconds are displayed by default" - end - end + with_default_realtime_interval controller do def scoped_collection diff --git a/config/initializers/yeti.rb b/config/initializers/yeti.rb index 720941f30..4ccd6f544 100644 --- a/config/initializers/yeti.rb +++ b/config/initializers/yeti.rb @@ -26,6 +26,7 @@ ActiveAdmin::ResourceDSL.send :include, ResourceDSL::ActsAsDelayedJobLock ActiveAdmin::ResourceDSL.send :include, ResourceDSL::ActsAsFilterByRoutingTagIds ActiveAdmin::ResourceDSL.send :include, ResourceDSL::ActsAsBelongsTo +ActiveAdmin::ResourceDSL.send :include, ResourceDSL::WithDefaultParams # ActiveAdmin::CSVBuilder.send(:include, Yeti::CSVBuilder) diff --git a/lib/resource_dsl/with_default_params.rb b/lib/resource_dsl/with_default_params.rb new file mode 100644 index 000000000..4a92ec98a --- /dev/null +++ b/lib/resource_dsl/with_default_params.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module ResourceDSL + module WithDefaultParams + # @param opts[:if] [Proc] - passes params into proc, block will be executed if proc returns true + # @param opts[:flash_type] [Proc] - passes params into proc, block will be executed if proc returns true + # yield in context of controller + # @yieldreturn message that will be shown in flash.now + def with_default_params(opts = {}, &block) + if_proc = opts[:if] || proc { |q: nil, **_| q.blank? } + flash_type = opts.fetch(:flash_type, :notice_message) + before_action only: [:index] do + if instance_exec(params.to_unsafe_h, &if_proc) + message = instance_exec(&block) + flash.now[flash_type] = message if message.present? + end + end + end + + def with_default_realtime_interval + with_default_params if: proc { |q: nil, **_| q.blank? || q[:time_interval_eq].blank? } do + params[:q] ||= {} + params[:q][:time_interval_eq] = Report::Realtime::Base::DEFAULT_INTERVAL + "Records for time interval #{Report::Realtime::Base::DEFAULT_INTERVAL} seconds are displayed by default" + end + end + end +end