Add calculation segment score

[MAILPOET-3533]
This commit is contained in:
Jan Lysý
2021-05-05 08:50:00 +02:00
committed by Veljko V
parent a92943ff30
commit 6d0486cfc5
5 changed files with 105 additions and 7 deletions

View File

@ -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')));

View File

@ -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() {

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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);
}