feat: better download for conversation traffic heatmap (#6755)

* feat: genearte report in a grid

* refactor: update API usage

* refactor: separate generate method

* refactor: abstract transform_data

* feat: annotate with comments

* feat: add explicit timezone

* feat: download data only in user timezone

* fix: dates included in heatmap
This commit is contained in:
Shivam Mishra
2023-04-11 09:40:54 +05:30
committed by GitHub
parent f4e121cc44
commit 17ff1f11a7
8 changed files with 114 additions and 38 deletions

View File

@@ -1,5 +1,7 @@
class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
include Api::V2::Accounts::ReportsHelper
include Api::V2::Accounts::HeatmapHelper
before_action :check_authorization
def index
@@ -34,6 +36,9 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
def conversation_traffic
@report_data = generate_conversations_heatmap_report
timezone_offset = (params[:timezone_offset] || 0).to_f
@timezone = ActiveSupport::TimeZone[timezone_offset]
generate_csv('conversation_traffic_reports', 'api/v2/accounts/reports/conversation_traffic')
end

View File

@@ -0,0 +1,103 @@
module Api::V2::Accounts::HeatmapHelper
def generate_conversations_heatmap_report
timezone_data = generate_heatmap_data_for_timezone(params[:timezone_offset])
group_traffic_data(timezone_data)
end
private
def group_traffic_data(data)
# start with an empty array
result_arr = []
# pick all the unique dates from the data in ascending order
dates = data.pluck(:date).uniq.sort
# add the dates as the first row, leave an empty cell for the hour column
# e.g. [nil, '2023-01-01', '2023-1-02', '2023-01-03']
result_arr << ([nil] + dates)
# group the data by hour, we do not need to sort it, because the data is already sorted
# given it starts from the beginning of the day
# here each hour is a key, and the value is an array of all the items for that hour at each date
# e.g. hour = 1
# value = [{date: 2023-01-01, value: 1}, {date: 2023-01-02, value: 1}, {date: 2023-01-03, value: 1}, ...]
data.group_by { |d| d[:hour] }.each do |hour, items|
# create a new row for each hour
row = [hour]
# group the items by date, so we can easily access the value for each date
# grouped values will be a hasg with the date as the key, and the value as the value
# e.g. { '2023-01-01' => [{date: 2023-01-01, value: 1}], '2023-01-02' => [{date: 2023-01-02, value: 1}], ... }
grouped_values = items.group_by { |d| d[:date] }
# now for each unique date we have, we can access the value for that date and append it to the array
dates.each do |date|
row << (grouped_values[date][0][:value] if grouped_values[date].is_a?(Array))
end
# row will look like [22, 0, 0, 1, 4, 6, 7, 4]
# add the row to the result array
result_arr << row
end
# return the resultant array
# the result looks like this
# [
# [nil, '2023-01-01', '2023-1-02', '2023-01-03'],
# [0, 0, 0, 0],
# [1, 0, 0, 0],
# [2, 0, 0, 0],
# [3, 0, 0, 0],
# [4, 0, 0, 0],
# ]
result_arr
end
def generate_heatmap_data_for_timezone(offset)
timezone = ActiveSupport::TimeZone[offset]&.name
timezone_today = DateTime.now.in_time_zone(timezone).beginning_of_day
timezone_data_raw = generate_heatmap_data(timezone_today, offset)
transform_data(timezone_data_raw, false)
end
def generate_heatmap_data(date, offset)
report_params = {
type: :account,
group_by: 'hour',
metric: 'conversations_count',
business_hours: false
}
V2::ReportBuilder.new(Current.account, report_params.merge({
since: since_timestamp(date),
until: until_timestamp(date),
timezone_offset: offset
})).build
end
def transform_data(data, zone_transform)
# rubocop:disable Rails/TimeZone
data.map do |d|
date = zone_transform ? Time.zone.at(d[:timestamp]) : Time.at(d[:timestamp])
{
date: date.to_date.to_s,
hour: date.hour,
value: d[:value]
}
end
# rubocop:enable Rails/TimeZone
end
def since_timestamp(date)
(date - 6.days).to_i.to_s
end
def until_timestamp(date)
date.to_i.to_s
end
end

View File

@@ -27,27 +27,6 @@ module Api::V2::Accounts::ReportsHelper
end
end
def generate_conversations_heatmap_report
report_params = {
type: :account,
group_by: 'hour',
since: params[:since],
until: params[:until],
metric: 'conversations_count',
business_hours: false
}
data = V2::ReportBuilder.new(Current.account, report_params).build
# data format is { timestamp: 1231242342, value: 3}
# we need to convert it to { date: "2020-01-01", hour: 12, value: 3}
#
# the generated report is **always** in UTC timezone
data.map do |d|
date = Time.zone.at(d[:timestamp]).to_s
[date, d[:value]]
end
end
def generate_report(report_params)
V2::ReportBuilder.new(
Current.account,

View File

@@ -59,9 +59,9 @@ class ReportsAPI extends ApiClient {
});
}
getConversationTrafficCSV({ from: since, to: until }) {
getConversationTrafficCSV() {
return axios.get(`${this.url}/conversation_traffic`, {
params: { since, until },
params: { timezone_offset: getTimeOffset() },
});
}

View File

@@ -142,10 +142,8 @@ export default {
},
downloadHeatmapData() {
let to = endOfDay(new Date());
let from = startOfDay(subDays(to, 6));
this.$store.dispatch('downloadAccountConversationHeatmap', {
from: getUnixTime(from),
to: getUnixTime(to),
});
},

View File

@@ -180,7 +180,7 @@ export const actions = {
});
},
downloadAccountConversationHeatmap(_, reportObj) {
Report.getConversationTrafficCSV(reportObj)
Report.getConversationTrafficCSV()
.then(response => {
downloadCsvFile(
generateFileName({

View File

@@ -1,12 +1,4 @@
<% headers = [
I18n.t('reports.conversation_traffic_csv.date'),
I18n.t('reports.conversation_traffic_csv.conversations_count'),
]
%>
<%= CSVSafe.generate_line headers -%>
<%= CSVSafe.generate_line [I18n.t('reports.conversation_traffic_csv.timezone'), @timezone] %>
<% @report_data.each do |row| %>
<%= CSVSafe.generate_line row -%>
<% end %>
<%= CSVSafe.generate_line [I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))] %>
<%= CSVSafe.generate_line [I18n.t('reports.utc_warning')] %>

View File

@@ -96,8 +96,7 @@ en:
avg_first_response_time: Avg first response time (Minutes)
avg_resolution_time: Avg resolution time (Minutes)
conversation_traffic_csv:
date: Date and time
conversations_count: No. of conversations
timezone: Timezone
default_group_by: day
csat:
headers: