From eb2ded6f65f43614f22a086092a42d233b47af7a Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Mon, 16 Nov 2020 19:41:52 +0530 Subject: [PATCH] feat: Agent & Inbox Report APIs (#1391) --- .../api/v2/accounts/reports_controller.rb | 12 ++++ app/javascript/dashboard/api/reports.js | 4 +- .../dashboard/store/modules/reports.js | 2 +- app/models/account.rb | 2 +- app/models/user.rb | 2 + .../api/v2/accounts/reports/agents.csv.erb | 12 ++++ .../api/v2/accounts/reports/inboxes.csv.erb | 12 ++++ config/routes.rb | 4 +- db/schema.rb | 2 - .../slack/send_on_slack_service.rb | 2 +- .../api/v2/accounts/report_controller_spec.rb | 60 ++++++++++++++++++- 11 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 app/views/api/v2/accounts/reports/agents.csv.erb create mode 100644 app/views/api/v2/accounts/reports/inboxes.csv.erb diff --git a/app/controllers/api/v2/accounts/reports_controller.rb b/app/controllers/api/v2/accounts/reports_controller.rb index c4f563d5d..3203f34e9 100644 --- a/app/controllers/api/v2/accounts/reports_controller.rb +++ b/app/controllers/api/v2/accounts/reports_controller.rb @@ -9,6 +9,18 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController render json: account_summary_metrics end + def agents + response.headers['Content-Type'] = 'text/csv' + response.headers['Content-Disposition'] = 'attachment; filename=agents_report.csv' + render layout: false, template: 'api/v2/accounts/reports/agents.csv.erb', format: 'csv' + end + + def inboxes + response.headers['Content-Type'] = 'text/csv' + response.headers['Content-Disposition'] = 'attachment; filename=inboxes_report.csv' + render layout: false, template: 'api/v2/accounts/reports/inboxes.csv.erb', format: 'csv' + end + private def account_summary_params diff --git a/app/javascript/dashboard/api/reports.js b/app/javascript/dashboard/api/reports.js index 8dedcc5d2..0773c769f 100644 --- a/app/javascript/dashboard/api/reports.js +++ b/app/javascript/dashboard/api/reports.js @@ -12,8 +12,8 @@ class ReportsAPI extends ApiClient { }); } - getAccountSummary(accountId, since, until) { - return axios.get(`${this.url}/${accountId}/account_summary`, { + getAccountSummary(since, until) { + return axios.get(`${this.url}/account_summary`, { params: { since, until }, }); } diff --git a/app/javascript/dashboard/store/modules/reports.js b/app/javascript/dashboard/store/modules/reports.js index 95f5df9e6..9d2dbe997 100644 --- a/app/javascript/dashboard/store/modules/reports.js +++ b/app/javascript/dashboard/store/modules/reports.js @@ -60,7 +60,7 @@ const actions = { }); }, fetchAccountSummary({ commit }, reportObj) { - Report.getAccountSummary(1, reportObj.from, reportObj.to) + Report.getAccountSummary(reportObj.from, reportObj.to) .then(accountSummary => { commit(types.default.SET_ACCOUNT_SUMMARY, accountSummary.data); }) diff --git a/app/models/account.rb b/app/models/account.rb index 465b2ca77..9b94194bd 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -10,7 +10,7 @@ # name :string not null # settings_flags :integer default(0), not null # support_email :string(100) -# timezone :string default("UTC") +# timezone :string default("UTC") # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/user.rb b/app/models/user.rb index 33faa8ee6..88c906431 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -66,6 +66,8 @@ class User < ApplicationRecord accepts_nested_attributes_for :account_users has_many :assigned_conversations, foreign_key: 'assignee_id', class_name: 'Conversation', dependent: :nullify + alias_attribute :conversations, :assigned_conversations + has_many :inbox_members, dependent: :destroy has_many :inboxes, through: :inbox_members, source: :inbox has_many :messages, as: :sender diff --git a/app/views/api/v2/accounts/reports/agents.csv.erb b/app/views/api/v2/accounts/reports/agents.csv.erb new file mode 100644 index 000000000..2986c6491 --- /dev/null +++ b/app/views/api/v2/accounts/reports/agents.csv.erb @@ -0,0 +1,12 @@ +<% headers = ['Agent name', 'Conversations count', 'Avg first response time (Minutes)', 'Avg resolution time (Minutes)'] %> +<%= CSV.generate_line headers %> +<% Current.account.users.each do |agent| %> + <% agent_report = V2::ReportBuilder.new(Current.account, { + type: :agent, + id: agent.id, + since: params[:since], + until: params[:until] + }).summary %> + <% row = [ agent.name, agent_report[:conversations_count], (agent_report[:avg_first_response_time]/60).to_i, (agent_report[:avg_resolution_time]/60).to_i ] %> +<%= CSV.generate_line row %> +<% end %> \ No newline at end of file diff --git a/app/views/api/v2/accounts/reports/inboxes.csv.erb b/app/views/api/v2/accounts/reports/inboxes.csv.erb new file mode 100644 index 000000000..24548449a --- /dev/null +++ b/app/views/api/v2/accounts/reports/inboxes.csv.erb @@ -0,0 +1,12 @@ +<% headers = ['Inbox name', 'Conversations count', 'Avg first response time (Minutes)', 'Avg resolution time (Minutes)'] %> +<%= CSV.generate_line headers %> +<% Current.account.inboxes.each do |inbox| %> + <% inbox_report = V2::ReportBuilder.new(Current.account, { + type: :inbox, + id: inbox.id, + since: params[:since], + until: params[:until] + }).summary %> + <% row = [ inbox.name, inbox_report[:conversations_count], (inbox_report[:avg_first_response_time]/60).to_i, (inbox_report[:avg_resolution_time]/60).to_i ] %> +<%= CSV.generate_line row %> +<% end %> \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 291373ca1..4bf472f01 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -145,9 +145,9 @@ Rails.application.routes.draw do resources :reports, only: [] do collection do get :account - end - member do get :account_summary + get :agents + get :inboxes end end end diff --git a/db/schema.rb b/db/schema.rb index 06f05b465..b0144fb09 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -456,11 +456,9 @@ ActiveRecord::Schema.define(version: 2020_10_27_135006) do t.index ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context" t.index ["taggable_id", "taggable_type", "tagger_id", "context"], name: "taggings_idy" t.index ["taggable_id"], name: "index_taggings_on_taggable_id" - t.index ["taggable_type", "taggable_id"], name: "index_taggings_on_taggable_type_and_taggable_id" t.index ["taggable_type"], name: "index_taggings_on_taggable_type" t.index ["tagger_id", "tagger_type"], name: "index_taggings_on_tagger_id_and_tagger_type" t.index ["tagger_id"], name: "index_taggings_on_tagger_id" - t.index ["tagger_type", "tagger_id"], name: "index_taggings_on_tagger_type_and_tagger_id" end create_table "tags", id: :serial, force: :cascade do |t| diff --git a/lib/integrations/slack/send_on_slack_service.rb b/lib/integrations/slack/send_on_slack_service.rb index 051ac0fcb..89c6d0b32 100644 --- a/lib/integrations/slack/send_on_slack_service.rb +++ b/lib/integrations/slack/send_on_slack_service.rb @@ -59,7 +59,7 @@ class Integrations::Slack::SendOnSlackService < Base::SendOnChannelService end def sender_type(sender) - sender.class == Contact ? 'Contact' : 'Agent' + sender.instance_of?(Contact) ? 'Contact' : 'Agent' end def update_reference_id diff --git a/spec/controllers/api/v2/accounts/report_controller_spec.rb b/spec/controllers/api/v2/accounts/report_controller_spec.rb index b4931f8a2..f72b6639a 100644 --- a/spec/controllers/api/v2/accounts/report_controller_spec.rb +++ b/spec/controllers/api/v2/accounts/report_controller_spec.rb @@ -46,10 +46,10 @@ RSpec.describe 'Reports API', type: :request do end end - describe 'GET /api/v2/accounts/:account_id/reports/:id/account_summary' do + describe 'GET /api/v2/accounts/:account_id/reports/account_summary' do context 'when it is an unauthenticated user' do it 'returns unauthorized' do - get "/api/v2/accounts/#{account.id}/reports/#{account.id}/account_summary" + get "/api/v2/accounts/#{account.id}/reports/account_summary" expect(response).to have_http_status(:unauthorized) end @@ -65,7 +65,7 @@ RSpec.describe 'Reports API', type: :request do until: Time.zone.today.to_time.to_i.to_s } - get "/api/v2/accounts/#{account.id}/reports/#{account.id}/account_summary", + get "/api/v2/accounts/#{account.id}/reports/account_summary", params: params, headers: agent.create_new_auth_token, as: :json @@ -77,4 +77,58 @@ RSpec.describe 'Reports API', type: :request do end end end + + describe 'GET /api/v2/accounts/:account_id/reports/agents' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + get "/api/v2/accounts/#{account.id}/reports/agents.csv" + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + let(:agent) { create(:user, account: account, role: :agent) } + + params = { + since: 30.days.ago.to_i.to_s, + until: Time.zone.today.to_time.to_i.to_s + } + + it 'returns summary' do + get "/api/v2/accounts/#{account.id}/reports/agents.csv", + params: params, + headers: agent.create_new_auth_token + + expect(response).to have_http_status(:success) + end + end + end + + describe 'GET /api/v2/accounts/:account_id/reports/inboxes' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + get "/api/v2/accounts/#{account.id}/reports/inboxes" + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + let(:agent) { create(:user, account: account, role: :agent) } + + params = { + since: 30.days.ago.to_i.to_s, + until: Time.zone.today.to_time.to_i.to_s + } + + it 'returns summary' do + get "/api/v2/accounts/#{account.id}/reports/inboxes", + params: params, + headers: agent.create_new_auth_token + + expect(response).to have_http_status(:success) + end + end + end end