2020-05-26 19:17:35 +03:00
|
|
|
import Service from '@ember/service';
|
2021-02-17 16:08:08 +03:00
|
|
|
import moment from 'moment';
|
2020-05-26 19:17:35 +03:00
|
|
|
import {inject as service} from '@ember/service';
|
2020-05-28 13:03:33 +03:00
|
|
|
import {task} from 'ember-concurrency-decorators';
|
2020-05-26 19:17:35 +03:00
|
|
|
import {tracked} from '@glimmer/tracking';
|
|
|
|
|
|
|
|
export default class MembersStatsService extends Service {
|
|
|
|
@service ajax;
|
|
|
|
@service ghostPaths;
|
2021-02-22 11:29:48 +03:00
|
|
|
@service store;
|
2020-05-26 19:17:35 +03:00
|
|
|
|
2020-06-01 17:48:46 +03:00
|
|
|
@tracked days = '30';
|
2020-05-26 19:17:35 +03:00
|
|
|
@tracked stats = null;
|
2021-02-18 17:17:10 +03:00
|
|
|
@tracked events = null;
|
2021-02-17 16:08:08 +03:00
|
|
|
@tracked countStats = null;
|
|
|
|
@tracked mrrStats = null;
|
2021-02-22 11:29:48 +03:00
|
|
|
@tracked newsletterStats = null;
|
2020-05-26 19:17:35 +03:00
|
|
|
|
2020-06-01 17:48:46 +03:00
|
|
|
fetch() {
|
|
|
|
let daysChanged = this._lastFetchedDays !== this.days;
|
2020-05-27 18:12:13 +03:00
|
|
|
let staleData = this._lastFetched && this._lastFetched - new Date() > 1 * 60 * 1000;
|
2020-05-28 13:03:33 +03:00
|
|
|
|
|
|
|
// return an already in-progress promise unless params have changed
|
|
|
|
if (this._fetchTask.isRunning && !this._forceRefresh && !daysChanged) {
|
|
|
|
return this._fetchTask.last;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return existing stats unless data is > 1 min old
|
2020-05-27 18:12:13 +03:00
|
|
|
if (this.stats && !this._forceRefresh && !daysChanged && !staleData) {
|
2020-05-26 19:17:35 +03:00
|
|
|
return Promise.resolve(this.stats);
|
|
|
|
}
|
|
|
|
|
2020-06-01 17:48:46 +03:00
|
|
|
return this._fetchTask.perform();
|
2020-05-26 19:17:35 +03:00
|
|
|
}
|
|
|
|
|
2021-02-23 15:44:17 +03:00
|
|
|
fetchTimeline(options = {}) {
|
2021-02-18 17:17:10 +03:00
|
|
|
let staleData = this._lastFetchedTimeline && this._lastFetchedTimeline - new Date() > 1 * 60 * 1000;
|
2021-02-23 15:44:17 +03:00
|
|
|
let differentLimit = this._lastFetchedTimelineLimit && this._lastFetchedTimelineLimit !== options.limit;
|
2021-02-18 17:17:10 +03:00
|
|
|
|
|
|
|
if (this._fetchTimelineTask.isRunning) {
|
|
|
|
return this._fetchTask.last;
|
|
|
|
}
|
|
|
|
|
2021-02-23 15:44:17 +03:00
|
|
|
if (this.events && !this._forceRefresh && !staleData && !differentLimit) {
|
2021-02-18 17:17:10 +03:00
|
|
|
return Promise.resolve(this.events);
|
|
|
|
}
|
|
|
|
|
2021-02-23 15:44:17 +03:00
|
|
|
return this._fetchTimelineTask.perform(options.limit);
|
2021-02-18 17:17:10 +03:00
|
|
|
}
|
|
|
|
|
2021-02-17 16:08:08 +03:00
|
|
|
fetchCounts() {
|
2021-02-18 20:13:51 +03:00
|
|
|
let staleData = this._lastFetchedCounts && this._lastFetchedCounts - new Date() > 1 * 60 * 1000;
|
2021-02-17 16:08:08 +03:00
|
|
|
|
|
|
|
// return an already in-progress promise unless params have changed
|
2021-02-18 20:13:51 +03:00
|
|
|
if (this._fetchCountsTask.isRunning) {
|
2021-02-17 16:08:08 +03:00
|
|
|
return this._fetchCountsTask.last;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return existing stats unless data is > 1 min old
|
2021-02-18 20:13:51 +03:00
|
|
|
if (this.countStats && !this._forceRefresh && !staleData) {
|
2021-02-22 11:30:11 +03:00
|
|
|
return Promise.resolve(this.countStats);
|
2021-02-17 16:08:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return this._fetchCountsTask.perform();
|
|
|
|
}
|
|
|
|
|
2021-02-22 11:29:48 +03:00
|
|
|
fetchNewsletterStats() {
|
|
|
|
let staleData = this._lastFetchedNewsletterStats && this._lastFetchedNewsletterStats - new Date() > 1 * 60 * 1000;
|
|
|
|
|
|
|
|
// return an already in-progress promise unless params have changed
|
|
|
|
if (this._fetchNewsletterStatsTask.isRunning) {
|
|
|
|
return this._fetchNewsletterStatsTask.last;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return existing stats unless data is > 1 min old
|
|
|
|
if (this.newsletterStats && !this._forceRefresh && !staleData) {
|
2021-02-25 11:04:34 +03:00
|
|
|
return Promise.resolve(this.newsletterStats);
|
2021-02-22 11:29:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return this._fetchNewsletterStatsTask.perform();
|
|
|
|
}
|
|
|
|
|
2021-02-25 18:32:50 +03:00
|
|
|
fillDates(data = []) {
|
2021-02-19 14:04:15 +03:00
|
|
|
let currentRangeDate = moment().subtract(30, 'days');
|
2021-02-17 16:08:08 +03:00
|
|
|
|
|
|
|
let endDate = moment().add(1, 'hour');
|
|
|
|
const output = {};
|
2021-02-19 08:48:01 +03:00
|
|
|
let lastVal = 0;
|
2021-02-17 16:08:08 +03:00
|
|
|
while (currentRangeDate.isBefore(endDate)) {
|
|
|
|
let dateStr = currentRangeDate.format('YYYY-MM-DD');
|
|
|
|
const dataOnDate = data.find(d => d.date === dateStr);
|
2021-02-19 08:48:01 +03:00
|
|
|
output[dateStr] = dataOnDate ? dataOnDate.value : lastVal;
|
|
|
|
lastVal = output[dateStr];
|
|
|
|
currentRangeDate = currentRangeDate.add(1, 'day');
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2021-02-25 18:32:50 +03:00
|
|
|
fillCountDates(data = {}) {
|
2021-02-19 14:04:15 +03:00
|
|
|
let currentRangeDate = moment().subtract(30, 'days');
|
2021-02-17 16:08:08 +03:00
|
|
|
|
2021-02-19 08:48:01 +03:00
|
|
|
let endDate = moment().add(1, 'hour');
|
|
|
|
const output = {};
|
2021-02-25 18:32:50 +03:00
|
|
|
const firstDateInRangeIndex = data.findIndex((val) => {
|
|
|
|
return moment(val.date).isAfter(currentRangeDate);
|
|
|
|
});
|
|
|
|
const initialDateInRangeVal = firstDateInRangeIndex > 0 ? data[firstDateInRangeIndex - 1] : null;
|
2021-02-19 08:48:01 +03:00
|
|
|
let lastVal = {
|
2021-02-25 18:32:50 +03:00
|
|
|
paid: initialDateInRangeVal ? initialDateInRangeVal.paid : 0,
|
|
|
|
free: initialDateInRangeVal ? initialDateInRangeVal.free : 0,
|
|
|
|
comped: initialDateInRangeVal ? initialDateInRangeVal.comped : 0,
|
|
|
|
total: initialDateInRangeVal ? (initialDateInRangeVal.paid + initialDateInRangeVal.free + initialDateInRangeVal.comped) : 0
|
2021-02-19 08:48:01 +03:00
|
|
|
};
|
|
|
|
while (currentRangeDate.isBefore(endDate)) {
|
|
|
|
let dateStr = currentRangeDate.format('YYYY-MM-DD');
|
|
|
|
const dataOnDate = data.find(d => d.date === dateStr);
|
|
|
|
output[dateStr] = dataOnDate ? {
|
|
|
|
paid: dataOnDate.paid,
|
|
|
|
free: dataOnDate.free,
|
|
|
|
comped: dataOnDate.comped,
|
|
|
|
total: dataOnDate.paid + dataOnDate.free + dataOnDate.comped
|
|
|
|
} : lastVal;
|
|
|
|
lastVal = output[dateStr];
|
2021-02-17 16:08:08 +03:00
|
|
|
currentRangeDate = currentRangeDate.add(1, 'day');
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
fetchMRR() {
|
2021-02-18 20:13:51 +03:00
|
|
|
let staleData = this._lastFetchedMRR && this._lastFetchedMRR - new Date() > 1 * 60 * 1000;
|
2021-02-17 16:08:08 +03:00
|
|
|
|
|
|
|
// return an already in-progress promise unless params have changed
|
2021-02-18 20:13:51 +03:00
|
|
|
if (this._fetchMRRTask.isRunning) {
|
2021-02-17 16:08:08 +03:00
|
|
|
return this._fetchMRRTask.last;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return existing stats unless data is > 1 min old
|
2021-02-18 20:13:51 +03:00
|
|
|
if (this.mrrStats && !this._forceRefresh && !staleData) {
|
2021-02-22 11:30:11 +03:00
|
|
|
return Promise.resolve(this.mrrStats);
|
2021-02-17 16:08:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return this._fetchMRRTask.perform();
|
|
|
|
}
|
|
|
|
|
2020-05-26 19:17:35 +03:00
|
|
|
invalidate() {
|
|
|
|
this._forceRefresh = true;
|
|
|
|
}
|
2020-05-28 13:03:33 +03:00
|
|
|
|
2021-02-22 11:29:48 +03:00
|
|
|
@task
|
|
|
|
*_fetchNewsletterStatsTask() {
|
2021-02-23 14:50:54 +03:00
|
|
|
const limit = 5;
|
2021-02-22 11:29:48 +03:00
|
|
|
let query = {
|
|
|
|
filter: 'email_count:-0',
|
|
|
|
order: 'submitted_at desc',
|
2021-02-23 14:50:54 +03:00
|
|
|
limit: limit
|
2021-02-22 11:29:48 +03:00
|
|
|
};
|
|
|
|
const results = yield this.store.query('email', query);
|
2021-02-23 00:45:59 +03:00
|
|
|
const data = results.toArray();
|
|
|
|
let stats = data.map((d) => {
|
2021-02-22 11:29:48 +03:00
|
|
|
return {
|
2021-02-23 00:45:59 +03:00
|
|
|
subject: d.subject,
|
|
|
|
submittedAt: moment(d.submittedAtUTC).format('YYYY-MM-DD'),
|
|
|
|
openRate: d.openRate
|
2021-02-22 11:29:48 +03:00
|
|
|
};
|
|
|
|
});
|
2021-02-23 00:18:36 +03:00
|
|
|
|
|
|
|
const paddedResults = [];
|
2021-02-23 14:50:54 +03:00
|
|
|
if (data.length < limit) {
|
|
|
|
const pad = limit - data.length;
|
2021-02-23 00:45:59 +03:00
|
|
|
const lastSubmittedAt = data.length > 0 ? data[results.length - 1].submittedAtUTC : moment();
|
2021-02-23 00:18:36 +03:00
|
|
|
for (let i = 0; i < pad; i++) {
|
|
|
|
paddedResults.push({
|
|
|
|
subject: '',
|
|
|
|
submittedAt: moment(lastSubmittedAt).subtract(i + 1, 'days').format('YYYY-MM-DD'),
|
|
|
|
openRate: 0
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2021-02-23 14:50:54 +03:00
|
|
|
stats = stats .concat(paddedResults);
|
2021-02-23 00:18:36 +03:00
|
|
|
stats.reverse();
|
2021-02-22 11:29:48 +03:00
|
|
|
this.newsletterStats = stats;
|
|
|
|
return stats;
|
|
|
|
}
|
|
|
|
|
2021-02-17 16:08:08 +03:00
|
|
|
@task
|
|
|
|
*_fetchCountsTask() {
|
2021-02-18 20:13:51 +03:00
|
|
|
this._lastFetchedCounts = new Date();
|
2021-02-17 16:08:08 +03:00
|
|
|
|
|
|
|
let statsUrl = this.ghostPaths.url.api('members/stats/count');
|
|
|
|
let stats = yield this.ajax.request(statsUrl);
|
|
|
|
this.countStats = stats;
|
|
|
|
return stats;
|
|
|
|
}
|
|
|
|
|
|
|
|
@task
|
|
|
|
*_fetchMRRTask() {
|
2021-02-18 20:13:51 +03:00
|
|
|
this._lastFetchedMRR = new Date();
|
2021-02-17 16:08:08 +03:00
|
|
|
|
|
|
|
let statsUrl = this.ghostPaths.url.api('members/stats/mrr');
|
|
|
|
let stats = yield this.ajax.request(statsUrl);
|
|
|
|
this.mrrStats = stats;
|
|
|
|
return stats;
|
|
|
|
}
|
|
|
|
|
2020-05-28 13:03:33 +03:00
|
|
|
@task
|
2020-06-01 17:48:46 +03:00
|
|
|
*_fetchTask() {
|
|
|
|
let {days} = this;
|
|
|
|
|
|
|
|
this._lastFetchedDays = days;
|
2020-05-28 13:03:33 +03:00
|
|
|
this._lastFetched = new Date();
|
|
|
|
this._forceRefresh = false;
|
|
|
|
|
|
|
|
let statsUrl = this.ghostPaths.url.api('members/stats');
|
|
|
|
let stats = yield this.ajax.request(statsUrl, {data: {days}});
|
|
|
|
this.stats = stats;
|
|
|
|
return stats;
|
|
|
|
}
|
2021-02-18 17:17:10 +03:00
|
|
|
|
|
|
|
@task
|
2021-02-23 15:44:17 +03:00
|
|
|
*_fetchTimelineTask(limit) {
|
2021-02-18 17:17:10 +03:00
|
|
|
this._lastFetchedTimeline = new Date();
|
2021-02-23 15:44:17 +03:00
|
|
|
this._lastFetchedTimelineLimit = limit;
|
2021-02-18 17:17:10 +03:00
|
|
|
let eventsUrl = this.ghostPaths.url.api('members/events');
|
2021-02-23 15:44:17 +03:00
|
|
|
let events = yield this.ajax.request(eventsUrl, {data: {limit}});
|
2021-02-18 17:17:10 +03:00
|
|
|
this.events = events;
|
|
|
|
return events;
|
|
|
|
}
|
2020-05-26 19:17:35 +03:00
|
|
|
}
|