mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-11-03 20:48:07 +00:00 
			
		
		
		
	feat: Agent & Inbox Report APIs (#1391)
This commit is contained in:
		@@ -9,6 +9,18 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
 | 
				
			|||||||
    render json: account_summary_metrics
 | 
					    render json: account_summary_metrics
 | 
				
			||||||
  end
 | 
					  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
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def account_summary_params
 | 
					  def account_summary_params
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,8 +12,8 @@ class ReportsAPI extends ApiClient {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getAccountSummary(accountId, since, until) {
 | 
					  getAccountSummary(since, until) {
 | 
				
			||||||
    return axios.get(`${this.url}/${accountId}/account_summary`, {
 | 
					    return axios.get(`${this.url}/account_summary`, {
 | 
				
			||||||
      params: { since, until },
 | 
					      params: { since, until },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,7 +60,7 @@ const actions = {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  fetchAccountSummary({ commit }, reportObj) {
 | 
					  fetchAccountSummary({ commit }, reportObj) {
 | 
				
			||||||
    Report.getAccountSummary(1, reportObj.from, reportObj.to)
 | 
					    Report.getAccountSummary(reportObj.from, reportObj.to)
 | 
				
			||||||
      .then(accountSummary => {
 | 
					      .then(accountSummary => {
 | 
				
			||||||
        commit(types.default.SET_ACCOUNT_SUMMARY, accountSummary.data);
 | 
					        commit(types.default.SET_ACCOUNT_SUMMARY, accountSummary.data);
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@
 | 
				
			|||||||
#  name                  :string           not null
 | 
					#  name                  :string           not null
 | 
				
			||||||
#  settings_flags        :integer          default(0), not null
 | 
					#  settings_flags        :integer          default(0), not null
 | 
				
			||||||
#  support_email         :string(100)
 | 
					#  support_email         :string(100)
 | 
				
			||||||
#  timezone       :string           default("UTC")
 | 
					#  timezone              :string           default("UTC")
 | 
				
			||||||
#  created_at            :datetime         not null
 | 
					#  created_at            :datetime         not null
 | 
				
			||||||
#  updated_at            :datetime         not null
 | 
					#  updated_at            :datetime         not null
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,6 +66,8 @@ class User < ApplicationRecord
 | 
				
			|||||||
  accepts_nested_attributes_for :account_users
 | 
					  accepts_nested_attributes_for :account_users
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  has_many :assigned_conversations, foreign_key: 'assignee_id', class_name: 'Conversation', dependent: :nullify
 | 
					  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 :inbox_members, dependent: :destroy
 | 
				
			||||||
  has_many :inboxes, through: :inbox_members, source: :inbox
 | 
					  has_many :inboxes, through: :inbox_members, source: :inbox
 | 
				
			||||||
  has_many :messages, as: :sender
 | 
					  has_many :messages, as: :sender
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								app/views/api/v2/accounts/reports/agents.csv.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/views/api/v2/accounts/reports/agents.csv.erb
									
									
									
									
									
										Normal file
									
								
							@@ -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 %>
 | 
				
			||||||
							
								
								
									
										12
									
								
								app/views/api/v2/accounts/reports/inboxes.csv.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/views/api/v2/accounts/reports/inboxes.csv.erb
									
									
									
									
									
										Normal file
									
								
							@@ -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 %>
 | 
				
			||||||
@@ -145,9 +145,9 @@ Rails.application.routes.draw do
 | 
				
			|||||||
        resources :reports, only: [] do
 | 
					        resources :reports, only: [] do
 | 
				
			||||||
          collection do
 | 
					          collection do
 | 
				
			||||||
            get :account
 | 
					            get :account
 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
          member do
 | 
					 | 
				
			||||||
            get :account_summary
 | 
					            get :account_summary
 | 
				
			||||||
 | 
					            get :agents
 | 
				
			||||||
 | 
					            get :inboxes
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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", "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", "taggable_type", "tagger_id", "context"], name: "taggings_idy"
 | 
				
			||||||
    t.index ["taggable_id"], name: "index_taggings_on_taggable_id"
 | 
					    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 ["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", "tagger_type"], name: "index_taggings_on_tagger_id_and_tagger_type"
 | 
				
			||||||
    t.index ["tagger_id"], name: "index_taggings_on_tagger_id"
 | 
					    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
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  create_table "tags", id: :serial, force: :cascade do |t|
 | 
					  create_table "tags", id: :serial, force: :cascade do |t|
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,7 +59,7 @@ class Integrations::Slack::SendOnSlackService < Base::SendOnChannelService
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def sender_type(sender)
 | 
					  def sender_type(sender)
 | 
				
			||||||
    sender.class == Contact ? 'Contact' : 'Agent'
 | 
					    sender.instance_of?(Contact) ? 'Contact' : 'Agent'
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def update_reference_id
 | 
					  def update_reference_id
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,10 +46,10 @@ RSpec.describe 'Reports API', type: :request do
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
  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
 | 
					    context 'when it is an unauthenticated user' do
 | 
				
			||||||
      it 'returns unauthorized' 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)
 | 
					        expect(response).to have_http_status(:unauthorized)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
@@ -65,7 +65,7 @@ RSpec.describe 'Reports API', type: :request do
 | 
				
			|||||||
          until: Time.zone.today.to_time.to_i.to_s
 | 
					          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,
 | 
					            params: params,
 | 
				
			||||||
            headers: agent.create_new_auth_token,
 | 
					            headers: agent.create_new_auth_token,
 | 
				
			||||||
            as: :json
 | 
					            as: :json
 | 
				
			||||||
@@ -77,4 +77,58 @@ RSpec.describe 'Reports API', type: :request do
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    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
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user