feat: Add live agent load report api (#4297)

This change allows the admin user to fetch conversation metrics for an account, agents, and filter conversation metrics for a specific agent.

Fixes #4305
This commit is contained in:
Aswin Dev P.S 2022-03-29 10:31:52 +05:30 committed by GitHub
parent ccf52a620b
commit 5e8fd689c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 455 additions and 119 deletions

View file

@ -1,5 +1,6 @@
class V2::ReportBuilder
include DateRangeHelper
include ReportHelper
attr_reader :account, :params
DEFAULT_GROUP_BY = 'day'.freeze
@ -40,23 +41,16 @@ class V2::ReportBuilder
}
end
private
def scope
case params[:type]
when :account
account
when :inbox
inbox
when :agent
user
when :label
label
when :team
team
def conversation_metrics
if params[:type].equal?(:account)
conversations
else
agent_metrics
end
end
private
def inbox
@inbox ||= account.inboxes.find(params[:id])
end
@ -84,47 +78,26 @@ class V2::ReportBuilder
)
end
def conversations_count
(get_grouped_values scope.conversations).count
def agent_metrics
users = @account.users
users = users.where(id: params[:user_id]) if params[:user_id].present?
users.each_with_object([]) do |user, arr|
@user = user
arr << {
user: { id: user.id, name: user.name, thumbnail: user.avatar_url },
metric: conversations
}
end
end
def incoming_messages_count
(get_grouped_values scope.messages.incoming.unscope(:order)).count
end
def outgoing_messages_count
(get_grouped_values scope.messages.outgoing.unscope(:order)).count
end
def resolutions_count
(get_grouped_values scope.conversations.resolved).count
end
def avg_first_response_time
(get_grouped_values scope.reporting_events.where(name: 'first_response')).average(:value)
end
def avg_resolution_time
(get_grouped_values scope.reporting_events.where(name: 'conversation_resolved')).average(:value)
end
def avg_resolution_time_summary
avg_rt = scope.reporting_events
.where(name: 'conversation_resolved', created_at: range)
.average(:value)
return 0 if avg_rt.blank?
avg_rt
end
def avg_first_response_time_summary
avg_frt = scope.reporting_events
.where(name: 'first_response', created_at: range)
.average(:value)
return 0 if avg_frt.blank?
avg_frt
def conversations
@open_conversations = scope.conversations.open
first_response_count = scope.reporting_events.where(name: 'first_response', conversation_id: @open_conversations.pluck('id')).count
metric = {
open: @open_conversations.count,
unattended: @open_conversations.count - first_response_count
}
metric[:unassigned] = @open_conversations.unassigned.count if params[:type].equal?(:account)
metric
end
end

View file

@ -35,6 +35,12 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
render layout: false, template: 'api/v2/accounts/reports/teams.csv.erb', format: 'csv'
end
def conversations
return head :unprocessable_entity if params[:type].blank?
render json: conversation_metrics
end
private
def check_authorization
@ -73,6 +79,13 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
}
end
def conversation_params
{
type: params[:type].to_sym,
user_id: params[:user_id]
}
end
def range
{
current: {
@ -91,4 +104,8 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
summary[:previous] = V2::ReportBuilder.new(Current.account, previous_summary_params).summary
summary
end
def conversation_metrics
V2::ReportBuilder.new(Current.account, conversation_params).conversation_metrics
end
end

View file

@ -0,0 +1,62 @@
module ReportHelper
private
def scope
case params[:type]
when :account
account
when :inbox
inbox
when :agent
user
when :label
label
when :team
team
end
end
def conversations_count
(get_grouped_values scope.conversations).count
end
def incoming_messages_count
(get_grouped_values scope.messages.incoming.unscope(:order)).count
end
def outgoing_messages_count
(get_grouped_values scope.messages.outgoing.unscope(:order)).count
end
def resolutions_count
(get_grouped_values scope.conversations.resolved).count
end
def avg_first_response_time
(get_grouped_values scope.reporting_events.where(name: 'first_response')).average(:value)
end
def avg_resolution_time
(get_grouped_values scope.reporting_events.where(name: 'conversation_resolved')).average(:value)
end
def avg_resolution_time_summary
avg_rt = scope.reporting_events
.where(name: 'conversation_resolved', created_at: range)
.average(:value)
return 0 if avg_rt.blank?
avg_rt
end
def avg_first_response_time_summary
avg_frt = scope.reporting_events
.where(name: 'first_response', created_at: range)
.average(:value)
return 0 if avg_frt.blank?
avg_frt
end
end

View file

@ -212,6 +212,7 @@ Rails.application.routes.draw do
get :inboxes
get :labels
get :teams
get :conversations
end
end
end

View file

@ -57,6 +57,68 @@ RSpec.describe 'Reports API', type: :request do
expect(current_day_metric.length).to eq(1)
expect(current_day_metric[0]['value']).to eq(10)
end
it 'return conversation metrics in account level' do
unassigned_conversation = create(:conversation, account: account, inbox: inbox,
assignee: nil, created_at: Time.zone.today)
unassigned_conversation.save!
get "/api/v2/accounts/#{account.id}/reports/conversations",
params: {
type: :account
},
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = JSON.parse(response.body)
expect(json_response['open']).to eq(11)
expect(json_response['unattended']).to eq(11)
expect(json_response['unassigned']).to eq(1)
end
it 'return conversation metrics for user in account level' do
create_list(:conversation, 2, account: account, inbox: inbox,
assignee: admin, created_at: Time.zone.today)
get "/api/v2/accounts/#{account.id}/reports/conversations",
params: {
type: :agent
},
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = JSON.parse(response.body)
expect(json_response.blank?).to be false
user_metrics = json_response.find { |item| item['user']['name'] == admin[:name] }
expect(user_metrics.present?).to be true
expect(user_metrics['metric']['open']).to eq(2)
expect(user_metrics['metric']['unattended']).to eq(2)
end
it 'return conversation metrics for specific user in account level' do
create_list(:conversation, 2, account: account, inbox: inbox,
assignee: admin, created_at: Time.zone.today)
get "/api/v2/accounts/#{account.id}/reports/conversations",
params: {
type: :agent,
user_id: user.id
},
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = JSON.parse(response.body)
expect(json_response.blank?).to be false
expect(json_response[0]['metric']['open']).to eq(10)
expect(json_response[0]['metric']['unattended']).to eq(10)
end
end
end

View file

@ -149,10 +149,11 @@ extended_message:
- $ref: ./resource/extension/message/with_source_sender.yml
## report list
report:
type: array
description: 'array of conversation count based on date'
items:
allOf:
- $ref: './resource/report.yml'
## report
account_summary:
$ref: './resource/reports/summary.yml'
agent_conversation_metrics:
$ref: './resource/reports/conversation/agent.yml'

View file

@ -0,0 +1,18 @@
type: object
properties:
user:
type: object
properties:
id:
type: number
name:
type: string
thumbnail:
type: string
metric:
type: object
properties:
open:
type: number
unattended:
type: number

View file

@ -0,0 +1,23 @@
tags:
- Reports
operationId: get-account-conversation-metrics
summary: Account Conversation Metrics
description: Get conversation metrics for Account
responses:
200:
description: Success
schema:
type: object
description: 'Object of account conversation metrics'
properties:
open:
type: number
unattended:
type: number
unassigned:
type: number
404:
description: reports not found
403:
description: Access denied

View file

@ -0,0 +1,18 @@
tags:
- Reports
operationId: get-agent-conversation-metrics
summary: Agent Conversation Metrics
description: Get conversation metrics for Agent
responses:
200:
description: Success
schema:
type: array
description: 'Array of agent based conversation metrics'
items:
$ref: '#/definitions/agent_conversation_metrics'
404:
description: reports not found
403:
description: Access denied

View file

@ -10,7 +10,12 @@ responses:
type: array
description: 'Array of date based conversation statistics'
items:
$ref: '#/definitions/report'
type: object
properties:
value:
type: string
timestamp:
type: number
404:
description: reports not found
403:

View file

@ -7,10 +7,8 @@ responses:
200:
description: Success
schema:
type: array
description: 'Array of date based conversation statistics'
items:
$ref: '#/definitions/report'
description: 'Object of summary metrics'
$ref: '#/definitions/account_summary'
404:
description: reports not found
403:

View file

@ -391,3 +391,35 @@
description: The timestamp from where report should stop.
get:
$ref: './application/reports/summary.yml'
# Conversation metrics for account
/api/v2/accounts/{account_id}/reports/conversations:
parameters:
- $ref: '#/parameters/account_id'
- in: query
name: type
type: string
enum:
- account
required: true
description: Type of report
get:
$ref: './application/reports/conversation/account.yml'
# Conversation metrics for agent
/api/v2/accounts/{account_id}/reports/conversations/:
parameters:
- $ref: '#/parameters/account_id'
- in: query
name: type
type: string
enum:
- agent
required: true
description: Type of report
- in: query
name: user_id
type: string
description: The numeric ID of the user
get:
$ref: './application/reports/conversation/agent.yml'

View file

@ -3604,7 +3604,15 @@
"type": "array",
"description": "Array of date based conversation statistics",
"items": {
"$ref": "#/definitions/report"
"type": "object",
"properties": {
"value": {
"type": "string"
},
"timestamp": {
"type": "number"
}
}
}
}
},
@ -3651,14 +3659,110 @@
"operationId": "list-all-conversation-statistics-summary",
"summary": "Get Account reports summary",
"description": "Get Account reports summary for a specific type and date range",
"responses": {
"200": {
"description": "Success",
"schema": {
"$ref": "#/definitions/account_summary"
}
},
"404": {
"description": "reports not found"
},
"403": {
"description": "Access denied"
}
}
}
},
"/api/v2/accounts/{account_id}/reports/conversations": {
"parameters": [
{
"$ref": "#/parameters/account_id"
},
{
"in": "query",
"name": "type",
"type": "string",
"enum": [
"account"
],
"required": true,
"description": "Type of report"
}
],
"get": {
"tags": [
"Reports"
],
"operationId": "get-account-conversation-metrics",
"summary": "Account Conversation Metrics",
"description": "Get conversation metrics for Account",
"responses": {
"200": {
"description": "Success",
"schema": {
"type": "object",
"description": "Object of account conversation metrics",
"properties": {
"open": {
"type": "number"
},
"unattended": {
"type": "number"
},
"unassigned": {
"type": "number"
}
}
}
},
"404": {
"description": "reports not found"
},
"403": {
"description": "Access denied"
}
}
}
},
"/api/v2/accounts/{account_id}/reports/conversations/": {
"parameters": [
{
"$ref": "#/parameters/account_id"
},
{
"in": "query",
"name": "type",
"type": "string",
"enum": [
"agent"
],
"required": true,
"description": "Type of report"
},
{
"in": "query",
"name": "user_id",
"type": "string",
"description": "The numeric ID of the user"
}
],
"get": {
"tags": [
"Reports"
],
"operationId": "get-agent-conversation-metrics",
"summary": "Agent Conversation Metrics",
"description": "Get conversation metrics for Agent",
"responses": {
"200": {
"description": "Success",
"schema": {
"type": "array",
"description": "Array of date based conversation statistics",
"description": "Array of agent based conversation metrics",
"items": {
"$ref": "#/definitions/report"
"$ref": "#/definitions/agent_conversation_metrics"
}
}
},
@ -4879,58 +4983,80 @@
}
]
},
"report": {
"type": "array",
"description": "array of conversation count based on date",
"items": {
"allOf": [
{
"type": "object",
"properties": {
"avg_first_response_time": {
"type": "string"
},
"avg_resolution_time": {
"type": "string"
},
"conversations_count": {
"type": "number"
},
"incoming_messages_count": {
"type": "number"
},
"outgoing_messages_count": {
"type": "number"
},
"resolutions_count": {
"type": "number"
},
"previous": {
"type": "object",
"properties": {
"avg_first_response_time": {
"type": "string"
},
"avg_resolution_time": {
"type": "string"
},
"conversations_count": {
"type": "number"
},
"incoming_messages_count": {
"type": "number"
},
"outgoing_messages_count": {
"type": "number"
},
"resolutions_count": {
"type": "number"
}
}
}
"account_summary": {
"type": "object",
"properties": {
"avg_first_response_time": {
"type": "string"
},
"avg_resolution_time": {
"type": "string"
},
"conversations_count": {
"type": "number"
},
"incoming_messages_count": {
"type": "number"
},
"outgoing_messages_count": {
"type": "number"
},
"resolutions_count": {
"type": "number"
},
"previous": {
"type": "object",
"properties": {
"avg_first_response_time": {
"type": "string"
},
"avg_resolution_time": {
"type": "string"
},
"conversations_count": {
"type": "number"
},
"incoming_messages_count": {
"type": "number"
},
"outgoing_messages_count": {
"type": "number"
},
"resolutions_count": {
"type": "number"
}
}
]
}
}
},
"agent_conversation_metrics": {
"type": "object",
"properties": {
"user": {
"type": "object",
"properties": {
"id": {
"type": "number"
},
"name": {
"type": "string"
},
"thumbnail": {
"type": "string"
}
}
},
"metric": {
"type": "object",
"properties": {
"open": {
"type": "number"
},
"unattended": {
"type": "number"
}
}
}
}
}
},