diff --git a/app/builders/v2/reports/agent_summary_builder.rb b/app/builders/v2/reports/agent_summary_builder.rb index 382c67bf5..9b4541aaa 100644 --- a/app/builders/v2/reports/agent_summary_builder.rb +++ b/app/builders/v2/reports/agent_summary_builder.rb @@ -2,52 +2,38 @@ class V2::Reports::AgentSummaryBuilder < V2::Reports::BaseSummaryBuilder pattr_initialize [:account!, :params!] def build - set_grouped_conversations_count - set_grouped_avg_reply_time - set_grouped_avg_first_response_time - set_grouped_avg_resolution_time + load_data prepare_report end private - def set_grouped_conversations_count - @grouped_conversations_count = Current.account.conversations.where(created_at: range).group('assignee_id').count + attr_reader :conversations_count, :resolved_count, + :avg_resolution_time, :avg_first_response_time, :avg_reply_time + + def fetch_conversations_count + account.conversations.where(created_at: range).group('assignee_id').count end - def set_grouped_avg_resolution_time - @grouped_avg_resolution_time = get_grouped_average(reporting_events.where(name: 'conversation_resolved')) + def prepare_report + account.account_users.map do |account_user| + build_agent_stats(account_user) + end end - def set_grouped_avg_first_response_time - @grouped_avg_first_response_time = get_grouped_average(reporting_events.where(name: 'first_response')) - end - - def set_grouped_avg_reply_time - @grouped_avg_reply_time = get_grouped_average(reporting_events.where(name: 'reply_time')) + def build_agent_stats(account_user) + user_id = account_user.user_id + { + id: user_id, + conversations_count: conversations_count[user_id] || 0, + resolved_conversations_count: resolved_count[user_id] || 0, + avg_resolution_time: avg_resolution_time[user_id], + avg_first_response_time: avg_first_response_time[user_id], + avg_reply_time: avg_reply_time[user_id] + } end def group_by_key :user_id end - - def reporting_events - @reporting_events ||= Current.account.reporting_events.where(created_at: range) - end - - def prepare_report - account.account_users.each_with_object([]) do |account_user, arr| - arr << { - id: account_user.user_id, - conversations_count: @grouped_conversations_count[account_user.user_id], - avg_resolution_time: @grouped_avg_resolution_time[account_user.user_id], - avg_first_response_time: @grouped_avg_first_response_time[account_user.user_id], - avg_reply_time: @grouped_avg_reply_time[account_user.user_id] - } - end - end - - def average_value_key - ActiveModel::Type::Boolean.new.cast(params[:business_hours]).present? ? :value_in_business_hours : :value - end end diff --git a/app/builders/v2/reports/base_summary_builder.rb b/app/builders/v2/reports/base_summary_builder.rb index 33fe5e400..4de65926d 100644 --- a/app/builders/v2/reports/base_summary_builder.rb +++ b/app/builders/v2/reports/base_summary_builder.rb @@ -1,17 +1,50 @@ class V2::Reports::BaseSummaryBuilder include DateRangeHelper + def build + load_data + prepare_report + end + private + def load_data + @conversations_count = fetch_conversations_count + @resolved_count = fetch_resolved_count + @avg_resolution_time = fetch_average_time('conversation_resolved') + @avg_first_response_time = fetch_average_time('first_response') + @avg_reply_time = fetch_average_time('reply_time') + end + + def reporting_events + @reporting_events ||= account.reporting_events.where(created_at: range) + end + + def fetch_conversations_count + # Override this method + end + + def fetch_average_time(event_name) + get_grouped_average(reporting_events.where(name: event_name)) + end + + def fetch_resolved_count + reporting_events.where(name: 'conversation_resolved').group(group_by_key).count + end + def group_by_key # Override this method end + def prepare_report + # Override this method + end + def get_grouped_average(events) events.group(group_by_key).average(average_value_key) end def average_value_key - params[:business_hours].present? ? :value_in_business_hours : :value + ActiveModel::Type::Boolean.new.cast(params[:business_hours]).present? ? :value_in_business_hours : :value end end diff --git a/app/builders/v2/reports/inbox_summary_builder.rb b/app/builders/v2/reports/inbox_summary_builder.rb index 489350670..e27385856 100644 --- a/app/builders/v2/reports/inbox_summary_builder.rb +++ b/app/builders/v2/reports/inbox_summary_builder.rb @@ -23,18 +23,6 @@ class V2::Reports::InboxSummaryBuilder < V2::Reports::BaseSummaryBuilder account.conversations.where(created_at: range).group(group_by_key).count end - def fetch_resolved_count - reporting_events.where(name: 'conversation_resolved').group(group_by_key).count - end - - def fetch_average_time(event_name) - get_grouped_average(reporting_events.where(name: event_name)) - end - - def reporting_events - @reporting_events ||= account.reporting_events.where(created_at: range) - end - def prepare_report account.inboxes.map do |inbox| build_inbox_stats(inbox) diff --git a/app/builders/v2/reports/team_summary_builder.rb b/app/builders/v2/reports/team_summary_builder.rb index 3d7e765b3..b98151bc6 100644 --- a/app/builders/v2/reports/team_summary_builder.rb +++ b/app/builders/v2/reports/team_summary_builder.rb @@ -1,49 +1,37 @@ class V2::Reports::TeamSummaryBuilder < V2::Reports::BaseSummaryBuilder pattr_initialize [:account!, :params!] - def build - set_grouped_conversations_count - set_grouped_avg_reply_time - set_grouped_avg_first_response_time - set_grouped_avg_resolution_time - prepare_report - end - private - def set_grouped_conversations_count - @grouped_conversations_count = Current.account.conversations.where(created_at: range).group('team_id').count - end + attr_reader :conversations_count, :resolved_count, + :avg_resolution_time, :avg_first_response_time, :avg_reply_time - def set_grouped_avg_resolution_time - @grouped_avg_resolution_time = get_grouped_average(reporting_events.where(name: 'conversation_resolved')) - end - - def set_grouped_avg_first_response_time - @grouped_avg_first_response_time = get_grouped_average(reporting_events.where(name: 'first_response')) - end - - def set_grouped_avg_reply_time - @grouped_avg_reply_time = get_grouped_average(reporting_events.where(name: 'reply_time')) + def fetch_conversations_count + account.conversations.where(created_at: range).group(:team_id).count end def reporting_events - @reporting_events ||= Current.account.reporting_events.where(created_at: range).joins(:conversation) + @reporting_events ||= account.reporting_events.where(created_at: range).joins(:conversation) + end + + def prepare_report + account.teams.map do |team| + build_team_stats(team) + end + end + + def build_team_stats(team) + { + id: team.id, + conversations_count: conversations_count[team.id] || 0, + resolved_conversations_count: resolved_count[team.id] || 0, + avg_resolution_time: avg_resolution_time[team.id], + avg_first_response_time: avg_first_response_time[team.id], + avg_reply_time: avg_reply_time[team.id] + } end def group_by_key 'conversations.team_id' end - - def prepare_report - account.teams.each_with_object([]) do |team, arr| - arr << { - id: team.id, - conversations_count: @grouped_conversations_count[team.id], - avg_resolution_time: @grouped_avg_resolution_time[team.id], - avg_first_response_time: @grouped_avg_first_response_time[team.id], - avg_reply_time: @grouped_avg_reply_time[team.id] - } - end - end end diff --git a/spec/builders/v2/reports/agent_summary_builder_spec.rb b/spec/builders/v2/reports/agent_summary_builder_spec.rb new file mode 100644 index 000000000..e550d9707 --- /dev/null +++ b/spec/builders/v2/reports/agent_summary_builder_spec.rb @@ -0,0 +1,143 @@ +require 'rails_helper' + +RSpec.describe V2::Reports::AgentSummaryBuilder do + let(:account) { create(:account) } + let(:user1) { create(:user, account: account, role: :agent) } + let(:user2) { create(:user, account: account, role: :agent) } + + let(:params) do + { + business_hours: business_hours, + since: 1.week.ago.beginning_of_day, + until: Time.current.end_of_day + } + end + let(:builder) { described_class.new(account: account, params: params) } + + describe '#build' do + context 'when there is team data' do + before do + c1 = create(:conversation, account: account, assignee: user1, created_at: Time.current) + c2 = create(:conversation, account: account, assignee: user2, created_at: Time.current) + create( + :reporting_event, + account: account, + conversation: c2, + user: user2, + name: 'conversation_resolved', + value: 50, + value_in_business_hours: 40, + created_at: Time.current + ) + create( + :reporting_event, + account: account, + conversation: c1, + user: user1, + name: 'first_response', + value: 20, + value_in_business_hours: 10, + created_at: Time.current + ) + create( + :reporting_event, + account: account, + conversation: c1, + user: user1, + name: 'reply_time', + value: 30, + value_in_business_hours: 15, + created_at: Time.current + ) + create( + :reporting_event, + account: account, + conversation: c1, + user: user1, + name: 'reply_time', + value: 40, + value_in_business_hours: 25, + created_at: Time.current + ) + end + + context 'when business hours is disabled' do + let(:business_hours) { false } + + it 'returns the correct team stats' do + report = builder.build + + expect(report).to eq( + [ + { + id: user1.id, + conversations_count: 1, + resolved_conversations_count: 0, + avg_resolution_time: nil, + avg_first_response_time: 20.0, + avg_reply_time: 35.0 + }, + { + id: user2.id, + conversations_count: 1, + resolved_conversations_count: 1, + avg_resolution_time: 50.0, + avg_first_response_time: nil, + avg_reply_time: nil + } + ] + ) + end + end + + context 'when business hours is enabled' do + let(:business_hours) { true } + + it 'uses business hours values' do + report = builder.build + + expect(report).to eq( + [ + { + id: user1.id, + conversations_count: 1, + resolved_conversations_count: 0, + avg_resolution_time: nil, + avg_first_response_time: 10.0, + avg_reply_time: 20.0 + }, + { + id: user2.id, + conversations_count: 1, + resolved_conversations_count: 1, + avg_resolution_time: 40.0, + avg_first_response_time: nil, + avg_reply_time: nil + } + ] + ) + end + end + end + + context 'when there is no team data' do + let!(:new_user) { create(:user, account: account, role: :agent) } + let(:business_hours) { false } + + it 'returns zero values' do + report = builder.build + + expect(report).to include( + { + id: new_user.id, + conversations_count: 0, + resolved_conversations_count: 0, + avg_resolution_time: nil, + avg_first_response_time: nil, + avg_reply_time: nil + } + ) + end + end + end +end diff --git a/spec/builders/v2/reports/team_summary_builder_spec.rb b/spec/builders/v2/reports/team_summary_builder_spec.rb new file mode 100644 index 000000000..0ce237729 --- /dev/null +++ b/spec/builders/v2/reports/team_summary_builder_spec.rb @@ -0,0 +1,138 @@ +require 'rails_helper' + +RSpec.describe V2::Reports::TeamSummaryBuilder do + let(:account) { create(:account) } + let(:team1) { create(:team, account: account, name: 'team-1') } + let(:team2) { create(:team, account: account, name: 'team-2') } + let(:params) do + { + business_hours: business_hours, + since: 1.week.ago.beginning_of_day, + until: Time.current.end_of_day + } + end + let(:builder) { described_class.new(account: account, params: params) } + + describe '#build' do + context 'when there is team data' do + before do + c1 = create(:conversation, account: account, team: team1, created_at: Time.current) + c2 = create(:conversation, account: account, team: team2, created_at: Time.current) + create( + :reporting_event, + account: account, + conversation: c2, + name: 'conversation_resolved', + value: 50, + value_in_business_hours: 40, + created_at: Time.current + ) + create( + :reporting_event, + account: account, + conversation: c1, + name: 'first_response', + value: 20, + value_in_business_hours: 10, + created_at: Time.current + ) + create( + :reporting_event, + account: account, + conversation: c1, + name: 'reply_time', + value: 30, + value_in_business_hours: 15, + created_at: Time.current + ) + create( + :reporting_event, + account: account, + conversation: c1, + name: 'reply_time', + value: 40, + value_in_business_hours: 25, + created_at: Time.current + ) + end + + context 'when business hours is disabled' do + let(:business_hours) { false } + + it 'returns the correct team stats' do + report = builder.build + + expect(report).to eq( + [ + { + id: team1.id, + conversations_count: 1, + resolved_conversations_count: 0, + avg_resolution_time: nil, + avg_first_response_time: 20.0, + avg_reply_time: 35.0 + }, + { + id: team2.id, + conversations_count: 1, + resolved_conversations_count: 1, + avg_resolution_time: 50.0, + avg_first_response_time: nil, + avg_reply_time: nil + } + ] + ) + end + end + + context 'when business hours is enabled' do + let(:business_hours) { true } + + it 'uses business hours values' do + report = builder.build + + expect(report).to eq( + [ + { + id: team1.id, + conversations_count: 1, + resolved_conversations_count: 0, + avg_resolution_time: nil, + avg_first_response_time: 10.0, + avg_reply_time: 20.0 + }, + { + id: team2.id, + conversations_count: 1, + resolved_conversations_count: 1, + avg_resolution_time: 40.0, + avg_first_response_time: nil, + avg_reply_time: nil + } + ] + ) + end + end + end + + context 'when there is no team data' do + let!(:new_team) { create(:team, account: account) } + let(:business_hours) { false } + + it 'returns zero values' do + report = builder.build + + expect(report).to include( + { + id: new_team.id, + conversations_count: 0, + resolved_conversations_count: 0, + avg_resolution_time: nil, + avg_first_response_time: nil, + avg_reply_time: nil + } + ) + end + end + end +end