Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add affected users chart #383

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hawk.api",
"version": "1.0.6",
"version": "1.0.7",
"main": "index.ts",
"license": "UNLICENSED",
"scripts": {
Expand Down
104 changes: 93 additions & 11 deletions src/models/eventsFactory.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getMidnightWithTimezoneOffset, getUTCMidnight } from '../utils/dates';
import { addTimezoneOffset, getMidnightWithTimezoneOffset, setUTCMidnight } from '../utils/dates';
import { groupBy } from '../utils/grouper';

const Factory = require('./modelFactory');
Expand Down Expand Up @@ -258,14 +258,14 @@ class EventsFactory extends Factory {
}

/**
* Fetch timestamps and total count of errors (or target error) for each day since
* Get daily events for last few days grouped by user timezone days
*
* @param {number} days - how many days we need to fetch for displaying in a chart
* @param {number} timezoneOffset - user's local timezone offset in minutes
* @param {string} groupHash - event's group hash for showing only target event
* @return {ProjectChartItem[]}
* @param {number} days - days to get
* @param {number} [timezoneOffset] - user timezone offset in minutes
* @param {string} [groupHash] - event group hash
* @return {Promise<object>}
*/
async findChartData(days, timezoneOffset = 0, groupHash = '') {
async getGroupedDailyEvents(days, timezoneOffset = 0, groupHash) {
const today = new Date();
const since = today.setDate(today.getDate() - days) / 1000;

Expand Down Expand Up @@ -305,17 +305,99 @@ class EventsFactory extends Factory {
* Group events using 'groupByTimestamp:NNNNNNNN' key
* @type {ProjectChartItem[]}
*/
const groupedData = groupBy('groupingTimestamp')(dailyEvents);
return groupBy('groupingTimestamp')(dailyEvents);
}

/**
* Fetch timestamps and count of affected users for each day since
*
* @param {number} days - how many days we need to fetch for displaying in a chart
* @param {number} timezoneOffset - user's local timezone offset in minutes
* @param {string} groupHash - event's group hash for showing only target event
* @return {AffectedUsersChartItem[]}
*/
async findAffectedUsersChart(days, timezoneOffset = 0, groupHash = '') {
const groupedData = await this.getGroupedDailyEvents(days, timezoneOffset, groupHash);

/**
* Now fill all requested days
*/
let result = [];

for (let i = 0; i < days; i++) {
const day = new Date();

/**
* Subtract timezone offset to get user`s local day
*
* @example 22:00 UTC 25.12 === 01:00 GMT+03 26.12, so local date is 26
*/
addTimezoneOffset(day, -timezoneOffset);

/**
* Set midnight for user`s local date
*/
setUTCMidnight(day);

/**
* Get date for the chart
*/
day.setDate(day.getDate() - i);

const dayMidnight = day / 1000;
const groupedEvents = groupedData[`groupingTimestamp:${dayMidnight}`];
const affectedUsers = groupedEvents ? groupedEvents.reduce((set, value) => new Set([...set, ...value.affectedUsers]), new Set()) : new Set();

result.push({
timestamp: dayMidnight,
count: affectedUsers.size,
});
}

/**
* Order by time ascendance
*/
result = result.sort((a, b) => a.timestamp - b.timestamp);

return result;
}

/**
* Fetch timestamps and total count of errors (or target error) for each day since
*
* @param {number} days - how many days we need to fetch for displaying in a chart
* @param {number} timezoneOffset - user's local timezone offset in minutes
* @param {string} groupHash - event's group hash for showing only target event
* @return {ProjectChartItem[]}
*/
async findChartData(days, timezoneOffset = 0, groupHash = '') {
const groupedData = await this.getGroupedDailyEvents(days, timezoneOffset, groupHash);
/**
* Now fill all requested days
*/
let result = [];

for (let i = 0; i < days; i++) {
const now = new Date();
const day = new Date(now.setDate(now.getDate() - i));
const dayMidnight = getUTCMidnight(day) / 1000;
const day = new Date();

/**
* Subtract timezone offset to get user`s local day
*
* @example 22:00 UTC 25.12 === 01:00 GMT+03 26.12, so local date is 26
*/
addTimezoneOffset(day, -timezoneOffset);

/**
* Set midnight for user`s local date
*/
setUTCMidnight(day);

/**
* Get date for the chart
*/
day.setDate(day.getDate() - i);

const dayMidnight = day / 1000;
const groupedEvents = groupedData[`groupingTimestamp:${dayMidnight}`];

result.push({
Expand Down
15 changes: 15 additions & 0 deletions src/resolvers/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,21 @@ module.exports = {
return factories.usersFactory.dataLoaders.userById.load(assignee);
},

/**
* Return chart data for target event affected users in last few days
*
* @param {string} projectId - event's project
* @param {string} groupHash - event's groupHash
* @param {number} days - how many days we need to fetch for displaying in a charts
* @param {number} timezoneOffset - user's local timezone offset in minutes
* @returns {Promise<ProjectChartItem[]>}
*/
async usersAffectedChart({ projectId, groupHash }, { days, timezoneOffset }) {
const factory = new EventsFactory(new ObjectID(projectId));

return factory.findAffectedUsersChart(days, timezoneOffset, groupHash);
},

/**
* Return chart data for target event occured in last few days
*
Expand Down
21 changes: 21 additions & 0 deletions src/typeDefs/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,21 @@ type Event {
"""
usersAffected: Int

"""
Return affected users chart data for the last few days
"""
usersAffectedChart(
"""
How many days we need to fetch for displaying in a chart
"""
days: Int! = 0

"""
User's local timezone offset in minutes
"""
timezoneOffset: Int! = 0
): [ChartDataItem!]! @requireAuth

"""
Return graph of the error rate for the last few days
"""
Expand Down Expand Up @@ -392,6 +407,12 @@ type DailyEventInfo {
Last event occurrence timestamp
"""
lastRepetitionTime: Float!


"""
Array of user's ids affected this day
"""
affectedUsers: [String!]
}

type Subscription {
Expand Down
23 changes: 22 additions & 1 deletion src/utils/dates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,28 @@ import { ObjectId } from 'mongodb';
* @param {Date} date - date object
*/
export function getUTCMidnight(date: Date): number {
return date.setUTCHours(0, 0, 0, 0);
const copy = new Date(date);

return copy.setUTCHours(0, 0, 0, 0);
}

/**
* Sets UTC midnight for a given date
*
* @param {Date} date — date to set midnight
*/
export function setUTCMidnight(date: Date): void {
date.setHours(0, 0, 0, 0);
}

/**
* Adds passed offset in minutes to a given date
*
* @param {Date} date — date to change
* @param {number} offset — offset in minutes
*/
export function addTimezoneOffset(date: Date, offset: number): void {
date.setTime(date.getTime() + offset * 60 * 1000);
}

/**
Expand Down