Add calculation segment score
[MAILPOET-3533]
This commit is contained in:
@ -103,6 +103,7 @@ class Settings extends APIEndpoint {
|
|||||||
|
|
||||||
public function recalculateSubscribersScore() {
|
public function recalculateSubscribersScore() {
|
||||||
$this->statisticsOpensRepository->resetSubscribersScoreCalculation();
|
$this->statisticsOpensRepository->resetSubscribersScoreCalculation();
|
||||||
|
$this->statisticsOpensRepository->resetNewslettersScoreCalculation();
|
||||||
$task = new ScheduledTaskEntity();
|
$task = new ScheduledTaskEntity();
|
||||||
$task->setType(SubscribersEngagementScore::TASK_TYPE);
|
$task->setType(SubscribersEngagementScore::TASK_TYPE);
|
||||||
$task->setScheduledAt(Carbon::createFromTimestamp($this->wp->currentTime('timestamp')));
|
$task->setScheduledAt(Carbon::createFromTimestamp($this->wp->currentTime('timestamp')));
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace MailPoet\Cron\Workers;
|
namespace MailPoet\Cron\Workers;
|
||||||
|
|
||||||
use MailPoet\Models\ScheduledTask;
|
use MailPoet\Models\ScheduledTask;
|
||||||
|
use MailPoet\Segments\SegmentsRepository;
|
||||||
use MailPoet\Statistics\StatisticsOpensRepository;
|
use MailPoet\Statistics\StatisticsOpensRepository;
|
||||||
use MailPoet\Subscribers\SubscribersRepository;
|
use MailPoet\Subscribers\SubscribersRepository;
|
||||||
use MailPoetVendor\Carbon\Carbon;
|
use MailPoetVendor\Carbon\Carbon;
|
||||||
@ -12,6 +13,9 @@ class SubscribersEngagementScore extends SimpleWorker {
|
|||||||
const BATCH_SIZE = 60;
|
const BATCH_SIZE = 60;
|
||||||
const TASK_TYPE = 'subscribers_engagement_score';
|
const TASK_TYPE = 'subscribers_engagement_score';
|
||||||
|
|
||||||
|
/** @var SegmentsRepository */
|
||||||
|
private $segmentsRepository;
|
||||||
|
|
||||||
/** @var StatisticsOpensRepository */
|
/** @var StatisticsOpensRepository */
|
||||||
private $statisticsOpensRepository;
|
private $statisticsOpensRepository;
|
||||||
|
|
||||||
@ -19,25 +23,47 @@ class SubscribersEngagementScore extends SimpleWorker {
|
|||||||
private $subscribersRepository;
|
private $subscribersRepository;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
SegmentsRepository $segmentsRepository,
|
||||||
StatisticsOpensRepository $statisticsOpensRepository,
|
StatisticsOpensRepository $statisticsOpensRepository,
|
||||||
SubscribersRepository $subscribersRepository
|
SubscribersRepository $subscribersRepository
|
||||||
) {
|
) {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
$this->segmentsRepository = $segmentsRepository;
|
||||||
$this->statisticsOpensRepository = $statisticsOpensRepository;
|
$this->statisticsOpensRepository = $statisticsOpensRepository;
|
||||||
$this->subscribersRepository = $subscribersRepository;
|
$this->subscribersRepository = $subscribersRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processTaskStrategy(ScheduledTask $task, $timer) {
|
public function processTaskStrategy(ScheduledTask $task, $timer) {
|
||||||
$subscribers = $this->subscribersRepository->findByUpdatedScoreNotInLastMonth(SubscribersEngagementScore::BATCH_SIZE);
|
$recalculatedSubscribersCount = $this->recalculateSubscribers();
|
||||||
|
if ($recalculatedSubscribersCount > 0) {
|
||||||
|
$this->scheduleImmediately();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$recalculatedSegmentsCount = $this->recalculateSegments();
|
||||||
|
if ($recalculatedSegmentsCount > 0) {
|
||||||
|
$this->scheduleImmediately();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->schedule();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function recalculateSubscribers(): int {
|
||||||
|
$subscribers = $this->subscribersRepository->findByUpdatedScoreNotInLastMonth(self::BATCH_SIZE);
|
||||||
foreach ($subscribers as $subscriber) {
|
foreach ($subscribers as $subscriber) {
|
||||||
$this->statisticsOpensRepository->recalculateSubscriberScore($subscriber);
|
$this->statisticsOpensRepository->recalculateSubscriberScore($subscriber);
|
||||||
}
|
}
|
||||||
if ($subscribers) {
|
return count($subscribers);
|
||||||
$this->scheduleImmediately();
|
}
|
||||||
} else {
|
|
||||||
$this->schedule();
|
private function recalculateSegments(): int {
|
||||||
|
$segments = $this->segmentsRepository->findByUpdatedScoreNotInLastMonth(self::BATCH_SIZE);
|
||||||
|
foreach ($segments as $segment) {
|
||||||
|
$this->statisticsOpensRepository->recalculateSegmentScore($segment);
|
||||||
}
|
}
|
||||||
return true;
|
return count($segments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNextRunDate() {
|
public function getNextRunDate() {
|
||||||
|
@ -184,4 +184,17 @@ class SegmentsRepository extends Repository {
|
|||||||
|
|
||||||
return $rows;
|
return $rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findByUpdatedScoreNotInLastMonth(int $limit): array {
|
||||||
|
$dateTime = (new Carbon())->subMonths(1);
|
||||||
|
return $this->entityManager->createQueryBuilder()
|
||||||
|
->select('s')
|
||||||
|
->from(SegmentEntity::class, 's')
|
||||||
|
->where('s.averageEngagementScoreUpdatedAt IS NULL')
|
||||||
|
->orWhere('s.averageEngagementScoreUpdatedAt < :dateTime')
|
||||||
|
->setParameter('dateTime', $dateTime)
|
||||||
|
->getQuery()
|
||||||
|
->setMaxResults($limit)
|
||||||
|
->getResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace MailPoet\Statistics;
|
namespace MailPoet\Statistics;
|
||||||
|
|
||||||
use MailPoet\Doctrine\Repository;
|
use MailPoet\Doctrine\Repository;
|
||||||
|
use MailPoet\Entities\SegmentEntity;
|
||||||
use MailPoet\Entities\StatisticsNewsletterEntity;
|
use MailPoet\Entities\StatisticsNewsletterEntity;
|
||||||
use MailPoet\Entities\StatisticsOpenEntity;
|
use MailPoet\Entities\StatisticsOpenEntity;
|
||||||
use MailPoet\Entities\SubscriberEntity;
|
use MailPoet\Entities\SubscriberEntity;
|
||||||
@ -58,4 +59,54 @@ class StatisticsOpensRepository extends Repository {
|
|||||||
->setParameter('verified', null)
|
->setParameter('verified', null)
|
||||||
->getQuery()->execute();
|
->getQuery()->execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function recalculateSegmentScore(SegmentEntity $segment): void {
|
||||||
|
$segment->setAverageEngagementScoreUpdatedAt(new \DateTimeImmutable());
|
||||||
|
$dateTime = (new Carbon())->subYear();
|
||||||
|
$newslettersSentCount = $this
|
||||||
|
->entityManager
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select('count(DISTINCT statisticsNewsletter.newsletter)')
|
||||||
|
->from(StatisticsNewsletterEntity::class, 'statisticsNewsletter')
|
||||||
|
->join('statisticsNewsletter.subscriber', 'subscriber')
|
||||||
|
->join('subscriber.subscriberSegments', 'subscriberSegments')
|
||||||
|
->where('subscriber.status = :subscribed')
|
||||||
|
->andWhere('subscriberSegments.segment = :segment')
|
||||||
|
->andWhere('subscriberSegments.status = :subscribed')
|
||||||
|
->andWhere('subscriber.deletedAt IS NULL')
|
||||||
|
->andWhere('subscriber.engagementScore IS NOT NULL')
|
||||||
|
->andWhere('statisticsNewsletter.sentAt > :dateTime')
|
||||||
|
->setParameter('segment', $segment)
|
||||||
|
->setParameter('subscribed', SubscriberEntity::STATUS_SUBSCRIBED)
|
||||||
|
->setParameter('dateTime', $dateTime)
|
||||||
|
->getQuery()
|
||||||
|
->getSingleScalarResult();
|
||||||
|
if ($newslettersSentCount < 3) {
|
||||||
|
$this->entityManager->flush();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$avgScore = $this
|
||||||
|
->entityManager
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select('avg(DISTINCT subscriber.engagementScore)')
|
||||||
|
->from(SubscriberEntity::class, 'subscriber')
|
||||||
|
->join('subscriber.subscriberSegments', 'subscriberSegments')
|
||||||
|
->where('subscriberSegments.segment = :segment')
|
||||||
|
->andWhere('subscriber.status = :subscribed')
|
||||||
|
->andWhere('subscriberSegments.status = :subscribed')
|
||||||
|
->andWhere('subscriber.engagementScore IS NOT NULL')
|
||||||
|
->setParameter('segment', $segment)
|
||||||
|
->setParameter('subscribed', SubscriberEntity::STATUS_SUBSCRIBED)
|
||||||
|
->getQuery()
|
||||||
|
->getSingleScalarResult();
|
||||||
|
$segment->setAverageEngagementScore((float)$avgScore);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resetNewslettersScoreCalculation(): void {
|
||||||
|
$this->entityManager->createQueryBuilder()->update(SegmentEntity::class, 's')
|
||||||
|
->set('s.averageEngagementScoreUpdatedAt', ':verified')
|
||||||
|
->setParameter('verified', null)
|
||||||
|
->getQuery()->execute();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,13 @@ class Opens {
|
|||||||
$queue->getId()
|
$queue->getId()
|
||||||
);
|
);
|
||||||
$this->statisticsOpensRepository->recalculateSubscriberScore($subscriber);
|
$this->statisticsOpensRepository->recalculateSubscriberScore($subscriber);
|
||||||
|
foreach ($newsletter->getNewsletterSegments() as $newsletterSegment) {
|
||||||
|
$segment = $newsletterSegment->getSegment();
|
||||||
|
if (!$segment) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->statisticsOpensRepository->recalculateSegmentScore($segment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $this->returnResponse($displayImage);
|
return $this->returnResponse($displayImage);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user