200 lines
5.5 KiB
Ruby
200 lines
5.5 KiB
Ruby
class Reports::RollupDataSource < Reports::DataSource
|
|
def timeseries
|
|
count_metric? ? count_timeseries : average_timeseries
|
|
end
|
|
|
|
def aggregate
|
|
count_metric? ? count_aggregate : average_aggregate
|
|
end
|
|
|
|
def summary
|
|
metric_results = summary_rows.index_by(&:dimension_id)
|
|
|
|
merge_summary_results(metric_results, summary_conversation_counts)
|
|
end
|
|
|
|
private
|
|
|
|
def count_timeseries
|
|
grouped_data = all_periods_in_range.index_with { 0 }
|
|
|
|
rollup_scope.each do |row|
|
|
date_key = normalized_period_key(row.date)
|
|
grouped_data[date_key] ||= 0
|
|
grouped_data[date_key] += row.count
|
|
end
|
|
|
|
results = grouped_data.map do |date_key, count|
|
|
{ value: count, timestamp: date_key.in_time_zone(timezone).to_i }
|
|
end
|
|
|
|
results.sort_by { |result| result[:timestamp] }
|
|
end
|
|
|
|
def average_timeseries
|
|
grouped_data = all_periods_in_range.index_with { { count: 0, sum_value: 0.0 } }
|
|
|
|
rollup_scope.each { |row| accumulate_average_row(grouped_data, row) }
|
|
|
|
results = grouped_data.map do |date_key, data|
|
|
{
|
|
value: data[:count].zero? ? 0 : data[:sum_value] / data[:count],
|
|
timestamp: date_key.in_time_zone(timezone).to_i,
|
|
count: data[:count]
|
|
}
|
|
end
|
|
|
|
results.sort_by { |result| result[:timestamp] }
|
|
end
|
|
|
|
def count_aggregate
|
|
rollup_scope.sum(:count).to_i
|
|
end
|
|
|
|
def average_aggregate
|
|
result = rollup_scope.pick(Arel.sql("SUM(count), SUM(#{rollup_value_column})"))
|
|
return nil if result.blank? || result[0].to_i.zero?
|
|
|
|
result[1].to_f / result[0].to_i
|
|
end
|
|
|
|
def rollup_scope
|
|
ReportingEventsRollup.where(
|
|
account_id: account.id,
|
|
metric: rollup_metric,
|
|
dimension_type: dimension_type,
|
|
dimension_id: dimension_id_for_rollup,
|
|
date: rollup_date_range
|
|
)
|
|
end
|
|
|
|
def summary_rows
|
|
ReportingEventsRollup.where(
|
|
account_id: account.id,
|
|
dimension_type: dimension_type,
|
|
date: rollup_date_range
|
|
).group(:dimension_id).select(*summary_select_fields)
|
|
end
|
|
|
|
def summary_conversation_counts
|
|
account.conversations
|
|
.where(created_at: range)
|
|
.group(summary_conversation_group_by_key)
|
|
.count
|
|
end
|
|
|
|
def merge_summary_results(metric_results, conversation_counts)
|
|
(metric_results.keys | conversation_counts.keys).index_with do |dimension_id|
|
|
summary_attributes_for(metric_results[dimension_id], conversation_counts[dimension_id])
|
|
end
|
|
end
|
|
|
|
def summary_select_fields
|
|
['dimension_id'] + summary_metrics.flat_map { |definition| summary_select_fields_for_metric(definition) }
|
|
end
|
|
|
|
def summary_select_fields_for_metric(definition)
|
|
return [sum_count_select(definition[:rollup_metric], definition[:summary_key])] if definition[:aggregate] == :count
|
|
|
|
[
|
|
sum_count_select(definition[:rollup_metric], summary_count_alias(definition)),
|
|
sum_value_select(definition[:rollup_metric], summary_sum_alias(definition))
|
|
]
|
|
end
|
|
|
|
def sum_count_select(rollup_metric_name, alias_name)
|
|
"SUM(CASE WHEN metric = '#{rollup_metric_name}' THEN count ELSE 0 END) as #{alias_name}"
|
|
end
|
|
|
|
def sum_value_select(rollup_metric_name, alias_name)
|
|
"SUM(CASE WHEN metric = '#{rollup_metric_name}' THEN #{rollup_value_column} ELSE 0 END) as #{alias_name}"
|
|
end
|
|
|
|
def summary_attributes_for(row, conversations_count = 0)
|
|
summary_metrics.each_with_object({ conversations_count: conversations_count.to_i }) do |definition, attributes|
|
|
attributes[definition[:summary_key]] = summary_value_for(row, definition)
|
|
end
|
|
end
|
|
|
|
def summary_value_for(row, definition)
|
|
return row&.public_send(definition[:summary_key]).to_i if definition[:aggregate] == :count
|
|
|
|
average_from(row&.public_send(summary_sum_alias(definition)), row&.public_send(summary_count_alias(definition)))
|
|
end
|
|
|
|
def summary_count_alias(definition)
|
|
"#{definition[:summary_key]}_count"
|
|
end
|
|
|
|
def summary_sum_alias(definition)
|
|
"#{definition[:summary_key]}_sum_value"
|
|
end
|
|
|
|
def dimension_id_for_rollup
|
|
dimension_type == 'account' ? account.id : scope.id
|
|
end
|
|
|
|
def summary_conversation_group_by_key
|
|
{
|
|
'account' => :account_id,
|
|
'agent' => :assignee_id,
|
|
'inbox' => :inbox_id,
|
|
'team' => :team_id
|
|
}[dimension_type]
|
|
end
|
|
|
|
def rollup_value_column
|
|
use_business_hours? ? :sum_value_business_hours : :sum_value
|
|
end
|
|
|
|
def rollup_date_range
|
|
tz = ActiveSupport::TimeZone[account.reporting_timezone]
|
|
start_date = range.first.in_time_zone(tz).to_date
|
|
end_date = (range.last - 1.second).in_time_zone(tz).to_date
|
|
start_date..end_date
|
|
end
|
|
|
|
def all_periods_in_range
|
|
current = normalized_period_key(rollup_date_range.first)
|
|
periods = []
|
|
|
|
while current <= rollup_date_range.last
|
|
periods << current
|
|
current = advance_period(current)
|
|
end
|
|
|
|
periods
|
|
end
|
|
|
|
def accumulate_average_row(grouped_data, row)
|
|
date_key = normalized_period_key(row.date)
|
|
grouped_data[date_key] ||= { count: 0, sum_value: 0.0 }
|
|
grouped_data[date_key][:count] += row.count
|
|
grouped_data[date_key][:sum_value] += row.public_send(rollup_value_column)
|
|
end
|
|
|
|
def normalized_period_key(date)
|
|
case group_by
|
|
when 'week' then date.beginning_of_week(:sunday)
|
|
when 'month' then date.beginning_of_month
|
|
when 'year' then date.beginning_of_year
|
|
else date
|
|
end
|
|
end
|
|
|
|
def advance_period(date)
|
|
case group_by
|
|
when 'week' then date + 1.week
|
|
when 'month' then date + 1.month
|
|
when 'year' then date + 1.year
|
|
else date + 1.day
|
|
end
|
|
end
|
|
|
|
def average_from(sum_value, count)
|
|
return nil if count.to_i.zero?
|
|
|
|
sum_value.to_f / count.to_i
|
|
end
|
|
end
|