Add calculation segment score
[MAILPOET-3533]
This commit is contained in:
@ -43,7 +43,7 @@ class Settings extends APIEndpoint {
|
||||
|
||||
/** @var StatisticsOpensRepository */
|
||||
private $statisticsOpensRepository;
|
||||
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS,
|
||||
];
|
||||
@ -103,6 +103,7 @@ class Settings extends APIEndpoint {
|
||||
|
||||
public function recalculateSubscribersScore() {
|
||||
$this->statisticsOpensRepository->resetSubscribersScoreCalculation();
|
||||
$this->statisticsOpensRepository->resetNewslettersScoreCalculation();
|
||||
$task = new ScheduledTaskEntity();
|
||||
$task->setType(SubscribersEngagementScore::TASK_TYPE);
|
||||
$task->setScheduledAt(Carbon::createFromTimestamp($this->wp->currentTime('timestamp')));
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace MailPoet\Cron\Workers;
|
||||
|
||||
use MailPoet\Models\ScheduledTask;
|
||||
use MailPoet\Segments\SegmentsRepository;
|
||||
use MailPoet\Statistics\StatisticsOpensRepository;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoetVendor\Carbon\Carbon;
|
||||
@ -12,6 +13,9 @@ class SubscribersEngagementScore extends SimpleWorker {
|
||||
const BATCH_SIZE = 60;
|
||||
const TASK_TYPE = 'subscribers_engagement_score';
|
||||
|
||||
/** @var SegmentsRepository */
|
||||
private $segmentsRepository;
|
||||
|
||||
/** @var StatisticsOpensRepository */
|
||||
private $statisticsOpensRepository;
|
||||
|
||||
@ -19,25 +23,47 @@ class SubscribersEngagementScore extends SimpleWorker {
|
||||
private $subscribersRepository;
|
||||
|
||||
public function __construct(
|
||||
SegmentsRepository $segmentsRepository,
|
||||
StatisticsOpensRepository $statisticsOpensRepository,
|
||||
SubscribersRepository $subscribersRepository
|
||||
) {
|
||||
parent::__construct();
|
||||
$this->segmentsRepository = $segmentsRepository;
|
||||
$this->statisticsOpensRepository = $statisticsOpensRepository;
|
||||
$this->subscribersRepository = $subscribersRepository;
|
||||
}
|
||||
|
||||
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) {
|
||||
$this->statisticsOpensRepository->recalculateSubscriberScore($subscriber);
|
||||
}
|
||||
if ($subscribers) {
|
||||
$this->scheduleImmediately();
|
||||
} else {
|
||||
$this->schedule();
|
||||
return count($subscribers);
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -184,4 +184,17 @@ class SegmentsRepository extends Repository {
|
||||
|
||||
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;
|
||||
|
||||
use MailPoet\Doctrine\Repository;
|
||||
use MailPoet\Entities\SegmentEntity;
|
||||
use MailPoet\Entities\StatisticsNewsletterEntity;
|
||||
use MailPoet\Entities\StatisticsOpenEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
@ -58,4 +59,54 @@ class StatisticsOpensRepository extends Repository {
|
||||
->setParameter('verified', null)
|
||||
->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()
|
||||
);
|
||||
$this->statisticsOpensRepository->recalculateSubscriberScore($subscriber);
|
||||
foreach ($newsletter->getNewsletterSegments() as $newsletterSegment) {
|
||||
$segment = $newsletterSegment->getSegment();
|
||||
if (!$segment) {
|
||||
continue;
|
||||
}
|
||||
$this->statisticsOpensRepository->recalculateSegmentScore($segment);
|
||||
}
|
||||
}
|
||||
return $this->returnResponse($displayImage);
|
||||
}
|
||||
|
Reference in New Issue
Block a user