diff --git a/app/builders/v2/report_builder.rb b/app/builders/v2/report_builder.rb index c2900c7e6..7f0dd470c 100644 --- a/app/builders/v2/report_builder.rb +++ b/app/builders/v2/report_builder.rb @@ -7,6 +7,9 @@ class V2::ReportBuilder def initialize(account, params) @account = account @params = params + + timezone_offset = (params[:timezone_offset] || 0).to_f + @timezone = ActiveSupport::TimeZone[timezone_offset]&.name end def timeseries @@ -64,60 +67,58 @@ class V2::ReportBuilder @team ||= account.teams.find(params[:id]) end + def get_grouped_values(object_scope) + object_scope.group_by_period( + params[:group_by] || DEFAULT_GROUP_BY, + :created_at, + default_value: 0, + range: range, + permit: %w[day week month year], + time_zone: @timezone + ) + end + def conversations_count - scope.conversations - .group_by_period(params[:group_by] || DEFAULT_GROUP_BY, - :created_at, range: range, default_value: 0, permit: %w[day week month year]) - .count + (get_grouped_values scope.conversations).count end def incoming_messages_count - scope.messages.incoming.unscope(:order) - .group_by_period(params[:group_by] || DEFAULT_GROUP_BY, - :created_at, range: range, default_value: 0, permit: %w[day week month year]) - .count + (get_grouped_values scope.messages.incoming.unscope(:order)).count end def outgoing_messages_count - scope.messages.outgoing.unscope(:order) - .group_by_period(params[:group_by] || DEFAULT_GROUP_BY, - :created_at, range: range, default_value: 0, permit: %w[day week month year]) - .count + (get_grouped_values scope.messages.outgoing.unscope(:order)).count end def resolutions_count - scope.conversations - .resolved - .group_by_period(params[:group_by] || DEFAULT_GROUP_BY, - :created_at, range: range, default_value: 0, permit: %w[day week month year]) - .count + (get_grouped_values scope.conversations.resolved).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) + (get_grouped_values scope.events.where(name: 'first_response')).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) + (get_grouped_values scope.events.where(name: 'conversation_resolved')).average(:value) 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_rt = scope.events + .where(name: 'conversation_resolved', created_at: range) + .average(:value) - (avg_resolution_time.values.sum / avg_resolution_time.values.length) + return 0 if avg_rt.blank? + + avg_rt end def avg_first_response_time_summary - return 0 if avg_first_response_time.values.empty? + avg_frt = scope.events + .where(name: 'first_response', created_at: range) + .average(:value) - (avg_first_response_time.values.sum / avg_first_response_time.values.length) + return 0 if avg_frt.blank? + + avg_frt end end diff --git a/app/controllers/api/v2/accounts/reports_controller.rb b/app/controllers/api/v2/accounts/reports_controller.rb index 9801d760d..a83611676 100644 --- a/app/controllers/api/v2/accounts/reports_controller.rb +++ b/app/controllers/api/v2/accounts/reports_controller.rb @@ -58,7 +58,8 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController since: params[:since], until: params[:until], id: params[:id], - group_by: params[:group_by] + group_by: params[:group_by], + timezone_offset: params[:timezone_offset] } end diff --git a/app/javascript/dashboard/api/reports.js b/app/javascript/dashboard/api/reports.js index dbb2bf08d..90f8b34ea 100644 --- a/app/javascript/dashboard/api/reports.js +++ b/app/javascript/dashboard/api/reports.js @@ -1,6 +1,8 @@ /* global axios */ import ApiClient from './ApiClient'; +const getTimeOffset = () => -new Date().getTimezoneOffset() / 60; + class ReportsAPI extends ApiClient { constructor() { super('reports', { accountScoped: true, apiVersion: 'v2' }); @@ -8,13 +10,27 @@ class ReportsAPI extends ApiClient { getReports(metric, since, until, type = 'account', id, group_by) { return axios.get(`${this.url}`, { - params: { metric, since, until, type, id, group_by }, + params: { + metric, + since, + until, + type, + id, + group_by, + timezone_offset: getTimeOffset(), + }, }); } getSummary(since, until, type = 'account', id, group_by) { return axios.get(`${this.url}/summary`, { - params: { since, until, type, id, group_by }, + params: { + since, + until, + type, + id, + group_by, + }, }); } diff --git a/app/javascript/dashboard/api/specs/reports.spec.js b/app/javascript/dashboard/api/specs/reports.spec.js index f3b66996a..efde84fe4 100644 --- a/app/javascript/dashboard/api/specs/reports.spec.js +++ b/app/javascript/dashboard/api/specs/reports.spec.js @@ -27,6 +27,7 @@ describe('#Reports API', () => { since: 1621103400, until: 1621621800, type: 'account', + timezone_offset: -0, }, }); }); diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/components/FilterSelector.vue b/app/javascript/dashboard/routes/dashboard/settings/reports/components/FilterSelector.vue index 2a1884bac..8df8beec3 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/reports/components/FilterSelector.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/reports/components/FilterSelector.vue @@ -50,6 +50,7 @@ import subDays from 'date-fns/subDays'; import startOfDay from 'date-fns/startOfDay'; import getUnixTime from 'date-fns/getUnixTime'; import { GROUP_BY_FILTER } from '../constants'; +import endOfDay from 'date-fns/endOfDay'; export default { components: { @@ -79,9 +80,9 @@ export default { }, to() { if (this.isDateRangeSelected) { - return this.fromCustomDate(this.customDateRange[1]); + return this.toCustomDate(this.customDateRange[1]); } - return this.fromCustomDate(new Date()); + return this.toCustomDate(new Date()); }, from() { if (this.isDateRangeSelected) { @@ -134,6 +135,9 @@ export default { fromCustomDate(date) { return getUnixTime(startOfDay(date)); }, + toCustomDate(date) { + return getUnixTime(endOfDay(date)); + }, changeDateSelection(selectedRange) { this.currentDateRangeSelection = selectedRange; this.onDateRangeChange(); diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/components/ReportFilters.vue b/app/javascript/dashboard/routes/dashboard/settings/reports/components/ReportFilters.vue index 28321cdba..2fd32ee69 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/reports/components/ReportFilters.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/reports/components/ReportFilters.vue @@ -148,13 +148,15 @@