diff --git a/app/listeners/reporting_event_listener.rb b/app/listeners/reporting_event_listener.rb index 9f683a97f..a4e51405c 100644 --- a/app/listeners/reporting_event_listener.rb +++ b/app/listeners/reporting_event_listener.rb @@ -20,6 +20,7 @@ class ReportingEventListener < BaseListener create_bot_resolved_event(conversation, reporting_event) reporting_event.save! + ReportingEvents::RollupService.perform(reporting_event) end def first_reply_created(event) @@ -41,6 +42,7 @@ class ReportingEventListener < BaseListener ) reporting_event.save! + ReportingEvents::RollupService.perform(reporting_event) end def reply_created(event) @@ -65,6 +67,7 @@ class ReportingEventListener < BaseListener event_end_time: message.created_at ) reporting_event.save! + ReportingEvents::RollupService.perform(reporting_event) end def conversation_bot_handoff(event) @@ -88,6 +91,7 @@ class ReportingEventListener < BaseListener event_end_time: conversation.updated_at ) reporting_event.save! + ReportingEvents::RollupService.perform(reporting_event) end def conversation_opened(event) @@ -139,5 +143,6 @@ class ReportingEventListener < BaseListener bot_resolved_event = reporting_event.dup bot_resolved_event.name = 'conversation_bot_resolved' bot_resolved_event.save! + ReportingEvents::RollupService.new(bot_resolved_event).perform end end diff --git a/app/services/reporting_events/rollup_service.rb b/app/services/reporting_events/rollup_service.rb new file mode 100644 index 000000000..cdcbf4b6f --- /dev/null +++ b/app/services/reporting_events/rollup_service.rb @@ -0,0 +1,139 @@ +module ReportingEvents::RollupService + def self.perform(reporting_event) + new(reporting_event).perform + end + + def self.new(reporting_event) + Performer.new(reporting_event) + end + + class Performer + def initialize(reporting_event) + @reporting_event = reporting_event + @account = reporting_event.account + end + + def perform + return unless rollup_enabled? + + dimensions.each do |dimension_type, dimension_id| + next if dimension_id.nil? + + metrics_for_event.each do |metric, metric_data| + upsert_rollup(dimension_type, dimension_id, metric, metric_data) + end + end + end + + private + + def rollup_enabled? + @account.reporting_timezone.present? + end + + def event_date + @event_date ||= @reporting_event.created_at.in_time_zone(@account.reporting_timezone).to_date + end + + def dimensions + { + account: @account.id, + agent: @reporting_event.user_id, + inbox: @reporting_event.inbox_id, + team: team_id + } + end + + def team_id + return nil if @reporting_event.conversation_id.blank? + + @team_id ||= @reporting_event.conversation&.team_id + end + + def metrics_for_event + case @reporting_event.name + when 'conversation_resolved' + conversation_resolved_metrics + when 'first_response' + first_response_metrics + when 'reply_time' + reply_time_metrics + when 'conversation_bot_resolved' + bot_resolutions_metrics + when 'conversation_bot_handoff' + bot_handoffs_metrics + else + {} + end + end + + def conversation_resolved_metrics + { + resolutions_count: { count: 1, sum_value: 0, sum_value_business_hours: 0 }, + resolution_time: { + count: 1, + sum_value: @reporting_event.value, + sum_value_business_hours: @reporting_event.value_in_business_hours + } + } + end + + def first_response_metrics + { + first_response: { + count: 1, + sum_value: @reporting_event.value, + sum_value_business_hours: @reporting_event.value_in_business_hours + } + } + end + + def reply_time_metrics + { + reply_time: { + count: 1, + sum_value: @reporting_event.value, + sum_value_business_hours: @reporting_event.value_in_business_hours + } + } + end + + def bot_resolutions_metrics + { + bot_resolutions_count: { count: 1, sum_value: 0, sum_value_business_hours: 0 } + } + end + + def bot_handoffs_metrics + { + bot_handoffs_count: { count: 1, sum_value: 0, sum_value_business_hours: 0 } + } + end + + def upsert_rollup(dimension_type, dimension_id, metric, metric_data) + # rubocop:disable Rails/SkipsModelValidations + ReportingEventRollup.upsert( + { + account_id: @account.id, + date: event_date, + dimension_type: dimension_type, + dimension_id: dimension_id, + metric: metric, + count: metric_data[:count], + sum_value: metric_data[:sum_value], + sum_value_business_hours: metric_data[:sum_value_business_hours], + created_at: Time.current, + updated_at: Time.current + }, + unique_by: [:account_id, :date, :dimension_type, :dimension_id, :metric], + on_duplicate: Arel.sql( + 'count = count + EXCLUDED.count, ' \ + 'sum_value = sum_value + EXCLUDED.sum_value, ' \ + 'sum_value_business_hours = sum_value_business_hours + EXCLUDED.sum_value_business_hours, ' \ + 'updated_at = EXCLUDED.updated_at' + ) + ) + # rubocop:enable Rails/SkipsModelValidations + end + end +end