0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-04-15 03:01:37 -05:00

Added admin mock stats for source attribution

refs https://github.com/TryGhost/Team/issues/1891

- adds basic mock stats data for member source attribution on dashboard
This commit is contained in:
Rishabh 2022-09-13 14:04:59 +05:30 committed by Rishabh Garg
parent a3a0a1c46c
commit e151791fe2
2 changed files with 160 additions and 36 deletions

View file

@ -137,11 +137,11 @@ export default class DashboardMocksService extends Service {
* This method generates new data and forces a reload for all the charts
* Might be better to move this code to a temporary mocking service
*/
updateMockedData({days}) {
updateMockedData({days}) {
const generateDays = days;
const startDate = new Date();
startDate.setDate(startDate.getDate() - generateDays + 1);
/**
* @type {MemberCountStat[]}
*/
@ -236,7 +236,7 @@ export default class DashboardMocksService extends Service {
let freeDelta = Math.max(0, this._updateGrow(freeGrowth));
viralCounter -= 1;
if (viralCounter <= 0) {
viralCounter = Math.floor(Math.random() * 900);
freeDelta += Math.floor(Math.random() * 20 * index);
@ -334,10 +334,79 @@ export default class DashboardMocksService extends Service {
};
this.emailsSent30d = Math.floor(days * 123 / 90);
this.membersLastSeen7d = Math.round(Math.random() * currentCounts.free / 2);
this.membersLastSeen30d = this.membersLastSeen7d + Math.round(Math.random() * currentCounts.free / 2);
this.membersAttributionSources7d = [
{
source: 'Twitter',
freeSignups: Math.floor(Math.random() * currentCounts.free / 3),
paidConversions: Math.floor(Math.random() * currentCounts.paid / 3)
},
{
source: 'Ghost Newsletter',
freeSignups: Math.floor(Math.random() * currentCounts.free / 3),
paidConversions: Math.floor(Math.random() * currentCounts.paid / 3)
},
{
source: 'Ghost Network',
freeSignups: Math.floor(Math.random() * currentCounts.free / 3),
paidConversions: Math.floor(Math.random() * currentCounts.paid / 3)
},
{
source: 'Direct',
freeSignups: Math.floor(Math.random() * currentCounts.free / 3),
paidConversions: Math.floor(Math.random() * currentCounts.paid / 3)
}
];
this.membersAttributionSources30d = [
{
source: 'Twitter',
freeSignups: Math.floor(Math.random() * currentCounts.free / 2),
paidConversions: Math.floor(Math.random() * currentCounts.paid / 2)
},
{
source: 'Ghost Newsletter',
freeSignups: Math.floor(Math.random() * currentCounts.free / 2),
paidConversions: Math.floor(Math.random() * currentCounts.paid / 2)
},
{
source: 'Ghost Network',
freeSignups: Math.floor(Math.random() * currentCounts.free / 2),
paidConversions: Math.floor(Math.random() * currentCounts.paid / 2)
},
{
source: 'Direct',
freeSignups: Math.floor(Math.random() * currentCounts.free / 2),
paidConversions: Math.floor(Math.random() * currentCounts.paid / 2)
}
];
this.membersAttributionSources90d = [
{
source: 'Twitter',
freeSignups: Math.floor(Math.random() * currentCounts.free / 20),
paidConversions: Math.floor(Math.random() * currentCounts.paid / 20)
},
{
source: 'Ghost Newsletter',
freeSignups: Math.floor(Math.random() * currentCounts.free / 20),
paidConversions: Math.floor(Math.random() * currentCounts.paid / 20)
},
{
source: 'Ghost Network',
freeSignups: Math.floor(Math.random() * currentCounts.free / 20),
paidConversions: Math.floor(Math.random() * currentCounts.paid / 20)
},
{
source: 'Direct',
freeSignups: Math.floor(Math.random() * currentCounts.free / 20),
paidConversions: Math.floor(Math.random() * currentCounts.paid / 20)
}
];
this.emailOpenRateStats = [];
if (days >= 7) {
this.emailOpenRateStats.push(

View file

@ -21,6 +21,14 @@ import {tracked} from '@glimmer/tracking';
* @property {number} paidCanceled Amount of canceled paid members
*/
/**
* @typedef AttributionCountStat
* @type {Object}
* @property {number} source Attribution Source
* @property {number} freeSignups Total free members signed up for this source
* @property {number} paidConversions Total paid conversions for this source
*/
/**
* @typedef MemberCounts
* @type {Object}
@ -107,6 +115,24 @@ export default class DashboardStatsService extends Service {
@tracked
membersLastSeen30d = null;
/**
* @type {AttributionCountStat[]} Count of Attribution sources in last 7 days
*/
@tracked
membersAttributionSources7d = null;
/**
* @type {AttributionCountStat[]} Count of Attribution sources in last 30 days
*/
@tracked
membersAttributionSources30d = null;
/**
* @type {AttributionCountStat[]} Count of Attribution sources in last 90 days
*/
@tracked
membersAttributionSources90d = null;
/**
* @type {?number} Number of members last seen in last 7 days (could differ if filtered by member status)
*/
@ -476,50 +502,77 @@ export default class DashboardStatsService extends Service {
});
}
loadMrrStats() {
if (this._loadMrrStats.isRunning) {
loadMemberAttributionStats() {
if (this._loadMemberAttributionStats.isRunning) {
// We need to explicitly wait for the already running task instead of dropping it and returning immediately
return this._loadMrrStats.last;
return this._loadMemberAttributionStats.last;
}
return this._loadMrrStats.perform();
return this._loadMemberAttributionStats.perform();
}
/**
* Loads the members attribution stats
*/
@task
*_loadMemberAttributionStats() {
this.membersAttributionSources7d = null;
this.membersAttributionSources30d = null;
this.membersAttributionSources90d = null;
if (this.dashboardMocks.enabled) {
yield this.dashboardMocks.waitRandom();
this.membersAttributionSources7d = this.dashboardMocks.membersAttributionSources7d;
this.membersAttributionSources30d = this.dashboardMocks.membersAttributionSources30d;
this.membersAttributionSources90d = this.dashboardMocks.membersAttributionSources90d;
return;
}
return;
}
loadMrrStats() {
if (this._loadMrrStats.isRunning) {
// We need to explicitly wait for the already running task instead of dropping it and returning immediately
return this._loadMrrStats.last;
}
return this._loadMrrStats.perform();
}
/**
* Loads the mrr graphs for the current chartDays days
*/
@task
*_loadMrrStats() {
this.mrrStats = null;
if (this.dashboardMocks.enabled) {
yield this.dashboardMocks.waitRandom();
if (this.dashboardMocks.mrrStats === null) {
return null;
}
this.mrrStats = this.dashboardMocks.mrrStats;
return;
}
*_loadMrrStats() {
this.mrrStats = null;
if (this.dashboardMocks.enabled) {
yield this.dashboardMocks.waitRandom();
if (this.dashboardMocks.mrrStats === null) {
return null;
}
this.mrrStats = this.dashboardMocks.mrrStats;
return;
}
let statsUrl = this.ghostPaths.url.api('stats/mrr');
let stats = yield this.ajax.request(statsUrl);
let statsUrl = this.ghostPaths.url.api('stats/mrr');
let stats = yield this.ajax.request(statsUrl);
// Only show the highest value currency and filter the other ones out
const totals = stats.meta.totals;
let currentMax = totals[0];
if (!currentMax) {
// No valid data
this.mrrStats = [];
return;
}
// Only show the highest value currency and filter the other ones out
const totals = stats.meta.totals;
let currentMax = totals[0];
if (!currentMax) {
// No valid data
this.mrrStats = [];
return;
}
for (const total of totals) {
if (total.mrr > currentMax.mrr) {
currentMax = total;
}
}
for (const total of totals) {
if (total.mrr > currentMax.mrr) {
currentMax = total;
}
}
const useCurrency = currentMax.currency;
this.mrrStats = stats.stats.filter(d => d.currency === useCurrency);
}
const useCurrency = currentMax.currency;
this.mrrStats = stats.stats.filter(d => d.currency === useCurrency);
}
loadLastSeen() {
// todo: add proper logic to prevent duplicate calls + reuse results if nothing has changed
@ -703,6 +756,7 @@ export default class DashboardStatsService extends Service {
await this._loadNewsletterSubscribers.cancelAll();
await this._loadEmailsSent.cancelAll();
await this._loadEmailOpenRateStats.cancelAll();
await this._loadMemberAttributionStats.cancelAll();
// Restart tasks
this.loadSiteStatus();
@ -717,6 +771,7 @@ export default class DashboardStatsService extends Service {
this.loadNewsletterSubscribers();
this.loadEmailsSent();
this.loadEmailOpenRateStats();
this.loadMemberAttributionStats();
}
/**