Compute change of global subscribed subscribers

[MAILPOET-4828]
This commit is contained in:
Rostislav Wolny
2023-01-20 13:29:09 +01:00
committed by Aschepikov
parent 6e99d5ec9d
commit 04307ca2a6
6 changed files with 70 additions and 26 deletions

View File

@@ -161,10 +161,24 @@ class HomepageDataController {
$listData[$list['id']]['unsubscribed'] = $list['count']; $listData[$list['id']]['unsubscribed'] = $list['count'];
} }
$subscribedCount = $this->subscribersRepository->getCountOfCreatedAfterWithStatues($thirtyDaysAgo, [SubscriberEntity::STATUS_SUBSCRIBED]);
$unsubscribedCount = $this->subscribersRepository->getCountOfUnsubscribedAfter($thirtyDaysAgo);
$subscribedSubscribersCount = $this->subscribersRepository->getCountOfSubscribersForStates([SubscriberEntity::STATUS_SUBSCRIBED]);
$subscribedSubscribers30DaysAgo = $subscribedSubscribersCount - $subscribedCount + $unsubscribedCount;
if ($subscribedSubscribers30DaysAgo > 0) {
$globalChangePercent = (($subscribedSubscribersCount - $subscribedSubscribers30DaysAgo) / $subscribedSubscribers30DaysAgo) * 100;
if (floor($globalChangePercent) !== (float)$globalChangePercent) {
$globalChangePercent = round($globalChangePercent, 1);
}
} else {
$globalChangePercent = $subscribedSubscribersCount * 100;
}
return [ return [
'global' => [ 'global' => [
'subscribed' => $this->subscribersRepository->getCountOfCreatedAfterWithStatues($thirtyDaysAgo, [SubscriberEntity::STATUS_SUBSCRIBED]), 'subscribed' => $subscribedCount,
'unsubscribed' => $this->subscribersRepository->getCountOfUnsubscribedAfter($thirtyDaysAgo), 'unsubscribed' => $unsubscribedCount,
'change' => $globalChangePercent,
], ],
'lists' => array_values($listData), 'lists' => array_values($listData),
]; ];

View File

@@ -51,18 +51,22 @@ class SubscribersRepository extends Repository {
} }
public function getTotalSubscribers(): int { public function getTotalSubscribers(): int {
return $this->getCountOfSubscribersForStates([
SubscriberEntity::STATUS_SUBSCRIBED,
SubscriberEntity::STATUS_UNCONFIRMED,
SubscriberEntity::STATUS_INACTIVE,
]);
}
public function getCountOfSubscribersForStates(array $states): int {
$query = $this->entityManager $query = $this->entityManager
->createQueryBuilder() ->createQueryBuilder()
->select('count(n.id)') ->select('count(n.id)')
->from(SubscriberEntity::class, 'n') ->from(SubscriberEntity::class, 'n')
->where('n.deletedAt IS NULL AND n.status IN (:statuses)') ->where('n.deletedAt IS NULL AND n.status IN (:statuses)')
->setParameter('statuses', [ ->setParameter('statuses', $states)
SubscriberEntity::STATUS_SUBSCRIBED,
SubscriberEntity::STATUS_UNCONFIRMED,
SubscriberEntity::STATUS_INACTIVE,
])
->getQuery(); ->getQuery();
return (int)$query->getSingleScalarResult(); return intval($query->getSingleScalarResult());
} }
public function invalidateTotalSubscribersCache(): void { public function invalidateTotalSubscribersCache(): void {
@@ -390,7 +394,8 @@ class SubscribersRepository extends Repository {
->select('COUNT(s.id)') ->select('COUNT(s.id)')
->from(StatisticsUnsubscribeEntity::class, 'su') ->from(StatisticsUnsubscribeEntity::class, 'su')
->join('su.subscriber', 's') ->join('su.subscriber', 's')
->where('s.createdAt > :unsubscribedAfter') ->where('s.createdAt <= :unsubscribedAfter')
->andWhere('su.createdAt > :unsubscribedAfter')
->andWhere('s.status = :status') ->andWhere('s.status = :status')
->andWhere('s.deletedAt IS NULL') ->andWhere('s.deletedAt IS NULL')
->setParameter('unsubscribedAfter', $unsubscribedAfter) ->setParameter('unsubscribedAfter', $unsubscribedAfter)

View File

@@ -815,11 +815,6 @@ parameters:
count: 1 count: 1
path: ../../lib/Subscribers/SubscribersRepository.php path: ../../lib/Subscribers/SubscribersRepository.php
-
message: "#^Cannot cast mixed to int\\.$#"
count: 1
path: ../../lib/Subscribers/SubscribersRepository.php
- -
message: "#^Method MailPoet\\\\Subscribers\\\\SubscribersRepository\\:\\:findIdsOfDeletedByEmails\\(\\) should return array\\<int\\> but returns array\\<array\\<string, int\\>\\>\\.$#" message: "#^Method MailPoet\\\\Subscribers\\\\SubscribersRepository\\:\\:findIdsOfDeletedByEmails\\(\\) should return array\\<int\\> but returns array\\<array\\<string, int\\>\\>\\.$#"
count: 1 count: 1

View File

@@ -815,11 +815,6 @@ parameters:
count: 1 count: 1
path: ../../lib/Subscribers/SubscribersRepository.php path: ../../lib/Subscribers/SubscribersRepository.php
-
message: "#^Cannot cast mixed to int\\.$#"
count: 1
path: ../../lib/Subscribers/SubscribersRepository.php
- -
message: "#^Method MailPoet\\\\Subscribers\\\\SubscribersRepository\\:\\:findIdsOfDeletedByEmails\\(\\) should return array\\<int\\> but returns array\\<array\\<string, int\\>\\>\\.$#" message: "#^Method MailPoet\\\\Subscribers\\\\SubscribersRepository\\:\\:findIdsOfDeletedByEmails\\(\\) should return array\\<int\\> but returns array\\<array\\<string, int\\>\\>\\.$#"
count: 1 count: 1

View File

@@ -814,11 +814,6 @@ parameters:
count: 1 count: 1
path: ../../lib/Subscribers/SubscribersRepository.php path: ../../lib/Subscribers/SubscribersRepository.php
-
message: "#^Cannot cast mixed to int\\.$#"
count: 1
path: ../../lib/Subscribers/SubscribersRepository.php
- -
message: "#^Method MailPoet\\\\Subscribers\\\\SubscribersRepository\\:\\:findIdsOfDeletedByEmails\\(\\) should return array\\<int\\> but returns array\\<array\\<string, int\\>\\>\\.$#" message: "#^Method MailPoet\\\\Subscribers\\\\SubscribersRepository\\:\\:findIdsOfDeletedByEmails\\(\\) should return array\\<int\\> but returns array\\<array\\<string, int\\>\\>\\.$#"
count: 1 count: 1

View File

@@ -195,8 +195,8 @@ class HomepageDataControllerTest extends \MailPoetTest {
$oldUnsubscribedStats->setCreatedAt($thirtyOneDaysAgo); $oldUnsubscribedStats->setCreatedAt($thirtyOneDaysAgo);
$this->entityManager->persist($oldUnsubscribedStats); $this->entityManager->persist($oldUnsubscribedStats);
$this->entityManager->flush(); $this->entityManager->flush();
// Freshly unsubscribed // Freshly unsubscribed (but created before the period)
$newUnsubscribed = (new Subscriber())->withCreatedAt($twentyNineDaysAgo)->withStatus(SubscriberEntity::STATUS_UNSUBSCRIBED)->create(); $newUnsubscribed = (new Subscriber())->withCreatedAt($thirtyOneDaysAgo)->withStatus(SubscriberEntity::STATUS_UNSUBSCRIBED)->create();
$newUnsubscribedStats = new StatisticsUnsubscribeEntity(null, null, $newUnsubscribed); $newUnsubscribedStats = new StatisticsUnsubscribeEntity(null, null, $newUnsubscribed);
$newUnsubscribedStats->setCreatedAt($twentyNineDaysAgo); $newUnsubscribedStats->setCreatedAt($twentyNineDaysAgo);
$this->entityManager->persist($newUnsubscribedStats); $this->entityManager->persist($newUnsubscribedStats);
@@ -207,6 +207,46 @@ class HomepageDataControllerTest extends \MailPoetTest {
expect($subscribersStats['global']['unsubscribed'])->equals(1); expect($subscribersStats['global']['unsubscribed'])->equals(1);
} }
public function testItFetchesCorrectGlobalSubscriberChange(): void {
$thirtyOneDaysAgo = Carbon::now()->subDays(31);
$twentyNineDaysAgo = Carbon::now()->subDays(29);
// 10 New Subscribers
for ($i = 0; $i < 10; $i++) {
(new Subscriber())->withCreatedAt($twentyNineDaysAgo)->withStatus(SubscriberEntity::STATUS_SUBSCRIBED)->create();
}
$subscribersStats = $this->homepageDataController->getPageData()['subscribersStats'];
expect($subscribersStats['global']['change'])->equals(1000);
// 10 New Subscribers + 5 Old Subscribers
for ($i = 0; $i < 5; $i++) {
(new Subscriber())->withCreatedAt($thirtyOneDaysAgo)->withStatus(SubscriberEntity::STATUS_SUBSCRIBED)->create();
}
$subscribersStats = $this->homepageDataController->getPageData()['subscribersStats'];
expect($subscribersStats['global']['change'])->equals(200);
// 10 New Subscribers + 6 Old Subscribers
(new Subscriber())->withCreatedAt($thirtyOneDaysAgo)->withStatus(SubscriberEntity::STATUS_SUBSCRIBED)->create();
$subscribersStats = $this->homepageDataController->getPageData()['subscribersStats'];
expect($subscribersStats['global']['change'])->equals( 166.7);
// 10 New Subscribers + 6 Old Subscribers + 10 New Unsubscribed
for ($i = 0; $i < 10; $i++) {
$unsubscribed = (new Subscriber())->withCreatedAt($thirtyOneDaysAgo)->withStatus(SubscriberEntity::STATUS_UNSUBSCRIBED)->create();
$this->entityManager->persist(new StatisticsUnsubscribeEntity(null, null, $unsubscribed));
$this->entityManager->flush();
}
$subscribersStats = $this->homepageDataController->getPageData()['subscribersStats'];
expect($subscribersStats['global']['change'])->equals( 0);
// 10 New Subscribers + 6 Old Subscribers + 11 New Unsubscribed
$unsubscribed = (new Subscriber())->withCreatedAt($thirtyOneDaysAgo)->withStatus(SubscriberEntity::STATUS_UNSUBSCRIBED)->create();
$this->entityManager->persist(new StatisticsUnsubscribeEntity(null, null, $unsubscribed));
$this->entityManager->flush();
$subscribersStats = $this->homepageDataController->getPageData()['subscribersStats'];
expect($subscribersStats['global']['change'])->equals( -5.9);
}
public function testItFetchesCorrectListLevelSubscribedStats(): void { public function testItFetchesCorrectListLevelSubscribedStats(): void {
$thirtyOneDaysAgo = Carbon::now()->subDays(31); $thirtyOneDaysAgo = Carbon::now()->subDays(31);
$twentyNineDaysAgo = Carbon::now()->subDays(29); $twentyNineDaysAgo = Carbon::now()->subDays(29);