feat: Add business hours in downloadable reports (#4674)
Co-authored-by: Aswin Dev P.S <aswindevps@gmail.com>
This commit is contained in:
parent
dceeb57a1d
commit
52fad886b8
13 changed files with 138 additions and 67 deletions
|
@ -1,4 +1,5 @@
|
||||||
class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
||||||
|
include Api::V2::Accounts::ReportsHelper
|
||||||
before_action :check_authorization
|
before_action :check_authorization
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@ -12,27 +13,23 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def agents
|
def agents
|
||||||
response.headers['Content-Type'] = 'text/csv'
|
@report_data = generate_agents_report
|
||||||
response.headers['Content-Disposition'] = 'attachment; filename=agents_report.csv'
|
generate_csv('agents_report', 'api/v2/accounts/reports/agents.csv.erb')
|
||||||
render layout: false, template: 'api/v2/accounts/reports/agents.csv.erb', format: 'csv'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def inboxes
|
def inboxes
|
||||||
response.headers['Content-Type'] = 'text/csv'
|
@report_data = generate_inboxes_report
|
||||||
response.headers['Content-Disposition'] = 'attachment; filename=inboxes_report.csv'
|
generate_csv('inboxes_report', 'api/v2/accounts/reports/inboxes.csv.erb')
|
||||||
render layout: false, template: 'api/v2/accounts/reports/inboxes.csv.erb', format: 'csv'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def labels
|
def labels
|
||||||
response.headers['Content-Type'] = 'text/csv'
|
@report_data = generate_labels_report
|
||||||
response.headers['Content-Disposition'] = 'attachment; filename=labels_report.csv'
|
generate_csv('labels_report', 'api/v2/accounts/reports/labels.csv.erb')
|
||||||
render layout: false, template: 'api/v2/accounts/reports/labels.csv.erb', format: 'csv'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def teams
|
def teams
|
||||||
response.headers['Content-Type'] = 'text/csv'
|
@report_data = generate_teams_report
|
||||||
response.headers['Content-Disposition'] = 'attachment; filename=teams_report.csv'
|
generate_csv('teams_report', 'api/v2/accounts/reports/teams.csv.erb')
|
||||||
render layout: false, template: 'api/v2/accounts/reports/teams.csv.erb', format: 'csv'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def conversations
|
def conversations
|
||||||
|
@ -43,6 +40,12 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def generate_csv(filename, template)
|
||||||
|
response.headers['Content-Type'] = 'text/csv'
|
||||||
|
response.headers['Content-Disposition'] = "attachment; filename=#{filename}.csv"
|
||||||
|
render layout: false, template: template, format: 'csv'
|
||||||
|
end
|
||||||
|
|
||||||
def check_authorization
|
def check_authorization
|
||||||
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
|
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
|
||||||
end
|
end
|
||||||
|
|
56
app/helpers/api/v2/accounts/reports_helper.rb
Normal file
56
app/helpers/api/v2/accounts/reports_helper.rb
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
module Api::V2::Accounts::ReportsHelper
|
||||||
|
def generate_agents_report
|
||||||
|
Current.account.users.map do |agent|
|
||||||
|
agent_report = generate_report({ type: :agent, id: agent.id })
|
||||||
|
[agent.name] + generate_readable_report_metrics(agent_report)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_inboxes_report
|
||||||
|
Current.account.inboxes.map do |inbox|
|
||||||
|
inbox_report = generate_report({ type: :inbox, id: inbox.id })
|
||||||
|
[inbox.name, inbox.channel&.name] + generate_readable_report_metrics(inbox_report)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_teams_report
|
||||||
|
Current.account.teams.map do |team|
|
||||||
|
team_report = generate_report({ type: :team, id: team.id })
|
||||||
|
[team.name] + generate_readable_report_metrics(team_report)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_labels_report
|
||||||
|
Current.account.labels.map do |label|
|
||||||
|
label_report = generate_report({ type: :label, id: label.id })
|
||||||
|
[label.title] + generate_readable_report_metrics(label_report)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_report(report_params)
|
||||||
|
V2::ReportBuilder.new(
|
||||||
|
Current.account,
|
||||||
|
report_params.merge(
|
||||||
|
{
|
||||||
|
since: params[:since],
|
||||||
|
until: params[:until],
|
||||||
|
business_hours: ActiveModel::Type::Boolean.new.cast(params[:business_hours])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).summary
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def generate_readable_report_metrics(report_metric)
|
||||||
|
[
|
||||||
|
report_metric[:conversations_count],
|
||||||
|
time_to_minutes(report_metric[:avg_first_response_time]),
|
||||||
|
time_to_minutes(report_metric[:avg_resolution_time])
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def time_to_minutes(time_in_seconds)
|
||||||
|
(time_in_seconds / 60).to_i
|
||||||
|
end
|
||||||
|
end
|
|
@ -53,27 +53,27 @@ class ReportsAPI extends ApiClient {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getAgentReports(since, until) {
|
getAgentReports({ from: since, to: until, businessHours }) {
|
||||||
return axios.get(`${this.url}/agents`, {
|
return axios.get(`${this.url}/agents`, {
|
||||||
params: { since, until },
|
params: { since, until, business_hours: businessHours },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getLabelReports(since, until) {
|
getLabelReports({ from: since, to: until, businessHours }) {
|
||||||
return axios.get(`${this.url}/labels`, {
|
return axios.get(`${this.url}/labels`, {
|
||||||
params: { since, until },
|
params: { since, until, business_hours: businessHours },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getInboxReports(since, until) {
|
getInboxReports({ from: since, to: until, businessHours }) {
|
||||||
return axios.get(`${this.url}/inboxes`, {
|
return axios.get(`${this.url}/inboxes`, {
|
||||||
params: { since, until },
|
params: { since, until, business_hours: businessHours },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getTeamReports(since, until) {
|
getTeamReports({ from: since, to: until, businessHours }) {
|
||||||
return axios.get(`${this.url}/teams`, {
|
return axios.get(`${this.url}/teams`, {
|
||||||
params: { since, until },
|
params: { since, until, business_hours: businessHours },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,20 +47,25 @@ describe('#Reports API', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getAgentReports', () => {
|
it('#getAgentReports', () => {
|
||||||
reportsAPI.getAgentReports(1621103400, 1621621800);
|
reportsAPI.getAgentReports({
|
||||||
|
from: 1621103400,
|
||||||
|
to: 1621621800,
|
||||||
|
businessHours: true,
|
||||||
|
});
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v2/reports/agents',
|
'/api/v2/reports/agents',
|
||||||
{
|
{
|
||||||
params: {
|
params: {
|
||||||
since: 1621103400,
|
since: 1621103400,
|
||||||
until: 1621621800,
|
until: 1621621800,
|
||||||
|
business_hours: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getLabelReports', () => {
|
it('#getLabelReports', () => {
|
||||||
reportsAPI.getLabelReports(1621103400, 1621621800);
|
reportsAPI.getLabelReports({ from: 1621103400, to: 1621621800 });
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v2/reports/labels',
|
'/api/v2/reports/labels',
|
||||||
{
|
{
|
||||||
|
@ -73,7 +78,7 @@ describe('#Reports API', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getInboxReports', () => {
|
it('#getInboxReports', () => {
|
||||||
reportsAPI.getInboxReports(1621103400, 1621621800);
|
reportsAPI.getInboxReports({ from: 1621103400, to: 1621621800 });
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v2/reports/inboxes',
|
'/api/v2/reports/inboxes',
|
||||||
{
|
{
|
||||||
|
@ -86,7 +91,7 @@ describe('#Reports API', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getTeamReports', () => {
|
it('#getTeamReports', () => {
|
||||||
reportsAPI.getTeamReports(1621103400, 1621621800);
|
reportsAPI.getTeamReports({ from: 1621103400, to: 1621621800 });
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v2/reports/teams',
|
'/api/v2/reports/teams',
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,5 +13,10 @@ export const downloadCsvFile = (fileName, content) => {
|
||||||
return link;
|
return link;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const generateFileName = ({ type, to }) =>
|
export const generateFileName = ({ type, to, businessHours = false }) => {
|
||||||
`${type}-report-${format(fromUnixTime(to), 'dd-MM-yyyy')}.csv`;
|
let name = `${type}-report-${format(fromUnixTime(to), 'dd-MM-yyyy')}`;
|
||||||
|
if (businessHours) {
|
||||||
|
name = `${name}-business-hours`;
|
||||||
|
}
|
||||||
|
return `${name}.csv`;
|
||||||
|
};
|
||||||
|
|
|
@ -5,5 +5,9 @@ describe('#generateFileName', () => {
|
||||||
expect(generateFileName({ type: 'csat', to: 1652812199 })).toEqual(
|
expect(generateFileName({ type: 'csat', to: 1652812199 })).toEqual(
|
||||||
'csat-report-17-05-2022.csv'
|
'csat-report-17-05-2022.csv'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
generateFileName({ type: 'csat', to: 1652812199, businessHours: true })
|
||||||
|
).toEqual('csat-report-17-05-2022-business-hours.csv');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -251,7 +251,7 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
downloadReports() {
|
downloadReports() {
|
||||||
const { from, to, type } = this;
|
const { from, to, type, businessHours } = this;
|
||||||
const dispatchMethods = {
|
const dispatchMethods = {
|
||||||
agent: 'downloadAgentReports',
|
agent: 'downloadAgentReports',
|
||||||
label: 'downloadLabelReports',
|
label: 'downloadLabelReports',
|
||||||
|
@ -259,8 +259,8 @@ export default {
|
||||||
team: 'downloadTeamReports',
|
team: 'downloadTeamReports',
|
||||||
};
|
};
|
||||||
if (dispatchMethods[type]) {
|
if (dispatchMethods[type]) {
|
||||||
const fileName = generateFileName({ type, to });
|
const fileName = generateFileName({ type, to, businessHours });
|
||||||
const params = { from, to, fileName };
|
const params = { from, to, fileName, businessHours };
|
||||||
this.$store.dispatch(dispatchMethods[type], params);
|
this.$store.dispatch(dispatchMethods[type], params);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -120,7 +120,7 @@ export const actions = {
|
||||||
commit(types.default.UPDATE_REPORT_AGENTS_STATUS, data);
|
commit(types.default.UPDATE_REPORT_AGENTS_STATUS, data);
|
||||||
},
|
},
|
||||||
downloadAgentReports(_, reportObj) {
|
downloadAgentReports(_, reportObj) {
|
||||||
return Report.getAgentReports(reportObj.from, reportObj.to)
|
return Report.getAgentReports(reportObj)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
downloadCsvFile(reportObj.fileName, response.data);
|
downloadCsvFile(reportObj.fileName, response.data);
|
||||||
})
|
})
|
||||||
|
@ -129,7 +129,7 @@ export const actions = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
downloadLabelReports(_, reportObj) {
|
downloadLabelReports(_, reportObj) {
|
||||||
return Report.getLabelReports(reportObj.from, reportObj.to)
|
return Report.getLabelReports(reportObj)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
downloadCsvFile(reportObj.fileName, response.data);
|
downloadCsvFile(reportObj.fileName, response.data);
|
||||||
})
|
})
|
||||||
|
@ -138,7 +138,7 @@ export const actions = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
downloadInboxReports(_, reportObj) {
|
downloadInboxReports(_, reportObj) {
|
||||||
return Report.getInboxReports(reportObj.from, reportObj.to)
|
return Report.getInboxReports(reportObj)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
downloadCsvFile(reportObj.fileName, response.data);
|
downloadCsvFile(reportObj.fileName, response.data);
|
||||||
})
|
})
|
||||||
|
@ -147,7 +147,7 @@ export const actions = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
downloadTeamReports(_, reportObj) {
|
downloadTeamReports(_, reportObj) {
|
||||||
return Report.getTeamReports(reportObj.from, reportObj.to)
|
return Report.getTeamReports(reportObj)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
downloadCsvFile(reportObj.fileName, response.data);
|
downloadCsvFile(reportObj.fileName, response.data);
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,14 +6,7 @@
|
||||||
]
|
]
|
||||||
%>
|
%>
|
||||||
<%= CSV.generate_line headers -%>
|
<%= CSV.generate_line headers -%>
|
||||||
<% Current.account.users.each do |agent| %>
|
<% @report_data.each do |row| %>
|
||||||
<% 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 -%>
|
<%= CSV.generate_line row -%>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= CSV.generate_line [I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))] %>
|
<%= CSV.generate_line [I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))] %>
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
<% headers = ['Inbox name', 'Conversations count', 'Avg first response time (Minutes)', 'Avg resolution time (Minutes)'] %>
|
<% headers = [
|
||||||
|
I18n.t('reports.inbox_csv.inbox_name'),
|
||||||
|
I18n.t('reports.inbox_csv.inbox_type'),
|
||||||
|
I18n.t('reports.inbox_csv.conversations_count'),
|
||||||
|
I18n.t('reports.inbox_csv.avg_first_response_time'),
|
||||||
|
I18n.t('reports.inbox_csv.avg_resolution_time')
|
||||||
|
]
|
||||||
|
%>
|
||||||
<%= CSV.generate_line headers -%>
|
<%= CSV.generate_line headers -%>
|
||||||
<% Current.account.inboxes.each do |inbox| %>
|
<% @report_data.each do |row| %>
|
||||||
<% 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 -%>
|
<%= CSV.generate_line row -%>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<%= CSV.generate_line [I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))] %>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<% headers = ['Label Title', 'Conversations count', 'Avg first response time (Minutes)', 'Avg resolution time (Minutes)'] %>
|
<% headers = [
|
||||||
|
I18n.t('reports.label_csv.label_title'),
|
||||||
|
I18n.t('reports.label_csv.conversations_count'),
|
||||||
|
I18n.t('reports.label_csv.avg_first_response_time'),
|
||||||
|
I18n.t('reports.label_csv.avg_resolution_time')
|
||||||
|
]
|
||||||
|
%>
|
||||||
<%= CSV.generate_line headers -%>
|
<%= CSV.generate_line headers -%>
|
||||||
<% Current.account.labels.each do |label| %>
|
<% @report_data.each do |row| %>
|
||||||
<% label_report = V2::ReportBuilder.new(Current.account, {
|
|
||||||
type: :label,
|
|
||||||
id: label.id,
|
|
||||||
since: params[:since],
|
|
||||||
until: params[:until]
|
|
||||||
}).summary %>
|
|
||||||
<% row = [ label.title, label_report[:conversations_count], (label_report[:avg_first_response_time]/60).to_i, (label_report[:avg_resolution_time]/60).to_i ] %>
|
|
||||||
<%= CSV.generate_line row -%>
|
<%= CSV.generate_line row -%>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<%= CSV.generate_line [I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))] %>
|
||||||
|
|
|
@ -6,14 +6,7 @@
|
||||||
]
|
]
|
||||||
%>
|
%>
|
||||||
<%= CSV.generate_line headers -%>
|
<%= CSV.generate_line headers -%>
|
||||||
<% Current.account.teams.each do |team| %>
|
<% @report_data.each do |row| %>
|
||||||
<% team_report = V2::ReportBuilder.new(Current.account, {
|
|
||||||
type: :team,
|
|
||||||
id: team.id,
|
|
||||||
since: params[:since],
|
|
||||||
until: params[:until]
|
|
||||||
}).summary %>
|
|
||||||
<% row = [ team.name, team_report[:conversations_count], (team_report[:avg_first_response_time]/60).to_i, (team_report[:avg_resolution_time]/60).to_i ] %>
|
|
||||||
<%= CSV.generate_line row -%>
|
<%= CSV.generate_line row -%>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= CSV.generate_line [I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))] %>
|
<%= CSV.generate_line [I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))] %>
|
||||||
|
|
|
@ -54,6 +54,17 @@ en:
|
||||||
conversations_count: Conversations count
|
conversations_count: Conversations count
|
||||||
avg_first_response_time: Avg first response time (Minutes)
|
avg_first_response_time: Avg first response time (Minutes)
|
||||||
avg_resolution_time: Avg resolution time (Minutes)
|
avg_resolution_time: Avg resolution time (Minutes)
|
||||||
|
inbox_csv:
|
||||||
|
inbox_name: Inbox name
|
||||||
|
inbox_type: Inbox type
|
||||||
|
conversations_count: No. of conversations
|
||||||
|
avg_first_response_time: Avg first response time (Minutes)
|
||||||
|
avg_resolution_time: Avg resolution time (Minutes)
|
||||||
|
label_csv:
|
||||||
|
label_title: Label
|
||||||
|
conversations_count: No. of conversations
|
||||||
|
avg_first_response_time: Avg first response time (Minutes)
|
||||||
|
avg_resolution_time: Avg resolution time (Minutes)
|
||||||
team_csv:
|
team_csv:
|
||||||
team_name: Team name
|
team_name: Team name
|
||||||
conversations_count: Conversations count
|
conversations_count: Conversations count
|
||||||
|
|
Loading…
Reference in a new issue