fix: Consider timezone in the reports (#4027)

Co-authored-by: Sojan Jose <sojan@pepalo.com>
This commit is contained in:
Pranav Raj S 2022-02-28 10:56:24 +05:30 committed by GitHub
parent 4ca66c1195
commit 9b615f11f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 73 additions and 46 deletions

View file

@ -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

View file

@ -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

View file

@ -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,
},
});
}

View file

@ -27,6 +27,7 @@ describe('#Reports API', () => {
since: 1621103400,
until: 1621621800,
type: 'account',
timezone_offset: -0,
},
});
});

View file

@ -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();

View file

@ -148,13 +148,15 @@
</div>
</template>
<script>
import WootDateRangePicker from 'dashboard/components/ui/DateRangePicker.vue';
const CUSTOM_DATE_RANGE_ID = 5;
import subDays from 'date-fns/subDays';
import startOfDay from 'date-fns/startOfDay';
import endOfDay from 'date-fns/endOfDay';
import getUnixTime from 'date-fns/getUnixTime';
import startOfDay from 'date-fns/startOfDay';
import subDays from 'date-fns/subDays';
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
import WootDateRangePicker from 'dashboard/components/ui/DateRangePicker.vue';
import { GROUP_BY_FILTER } from '../constants';
const CUSTOM_DATE_RANGE_ID = 5;
export default {
components: {
@ -194,9 +196,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) {
@ -253,6 +255,7 @@ export default {
},
methods: {
onDateRangeChange() {
console.log(this.from, this.to);
this.$emit('date-range-change', {
from: this.from,
to: this.to,
@ -262,6 +265,9 @@ export default {
fromCustomDate(date) {
return getUnixTime(startOfDay(date));
},
toCustomDate(date) {
return getUnixTime(endOfDay(date));
},
changeDateSelection(selectedRange) {
this.currentDateRangeSelection = selectedRange;
this.onDateRangeChange();

View file

@ -1,9 +1,6 @@
/* eslint no-console: 0 */
/* eslint no-param-reassign: 0 */
/* eslint no-shadow: 0 */
import compareAsc from 'date-fns/compareAsc';
import fromUnixTime from 'date-fns/fromUnixTime';
import * as types from '../mutation-types';
import Report from '../../api/reports';
@ -48,7 +45,8 @@ export const actions = {
).then(accountReport => {
let { data } = accountReport;
data = data.filter(
el => compareAsc(new Date(), fromUnixTime(el.timestamp)) > -1
el =>
reportObj.to - el.timestamp > 0 && el.timestamp - reportObj.from >= 0
);
if (
reportObj.metric === 'avg_first_response_time' ||