class V2::ReportBuilder attr_reader :account, :params def initialize(account, params) @account = account @params = params end def timeseries send(params[:metric]) end # For backward compatible with old report def build timeseries.each_with_object([]) do |p, arr| arr << { value: p[1], timestamp: p[0].to_time.to_i } end end def summary { conversations_count: conversations_count.values.sum, incoming_messages_count: incoming_messages_count.values.sum, outgoing_messages_count: outgoing_messages_count.values.sum, avg_first_response_time: avg_first_response_time_summary, avg_resolution_time: avg_resolution_time_summary, resolutions_count: resolutions_count.values.sum } end private def scope return account if params[:type].match?('account') return inbox if params[:type].match?('inbox') return user if params[:type].match?('agent') end def inbox @inbox ||= account.inboxes.where(id: params[:id]).first end def user @user ||= account.users.where(id: params[:id]).first end def conversations_count scope.conversations .group_by_day(:created_at, range: range, default_value: 0) .count end # unscoped removes all scopes added to a model previously def incoming_messages_count scope.messages.unscoped.where(account_id: account.id).incoming .group_by_day(:created_at, range: range, default_value: 0) .count end def outgoing_messages_count scope.messages.unscoped.where(account_id: account.id).outgoing .group_by_day(:created_at, range: range, default_value: 0) .count end def resolutions_count scope.conversations .resolved .group_by_day(:created_at, range: range, default_value: 0) .count end def avg_first_response_time scope.events .where(name: 'first_response') .group_by_day(:created_at, range: range, default_value: 0) .average(:value) end def avg_resolution_time scope.events.where(name: 'conversation_resolved') .group_by_day(:created_at, range: range, default_value: 0) .average(:value) end def range parse_date_time(params[:since])..parse_date_time(params[:until]) end # Taking average of average is not too accurate # https://en.wikipedia.org/wiki/Simpson's_paradox # TODO: Will optimize this later def avg_resolution_time_summary return 0 if avg_resolution_time.values.empty? (avg_resolution_time.values.sum / avg_resolution_time.values.length) end def avg_first_response_time_summary return 0 if avg_first_response_time.values.empty? (avg_first_response_time.values.sum / avg_first_response_time.values.length) end def parse_date_time(datetime) return datetime if datetime.is_a?(DateTime) return datetime.to_datetime if datetime.is_a?(Time) || datetime.is_a?(Date) DateTime.strptime(datetime, '%s') end end