File: /home/xedaptot/ai.naniguide.com/app/Jobs/EvaluateAbTestWinner.php
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Model\AbTest;
use App\Model\AbTestVariant;
use App\Model\TrackingLog;
use App\Model\OpenLog;
use App\Model\ClickLog;
class EvaluateAbTestWinner implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 120;
public $tries = 1;
public function __construct(protected int $abTestId) {}
public function handle(): void
{
$abTest = AbTest::with(['variants.email', 'campaign'])->find($this->abTestId);
if (!$abTest || !$abTest->isAwaitingWinner()) {
return;
}
if ($abTest->winner_criteria === AbTest::CRITERIA_MANUAL) {
return; // Human picks winner via UI
}
$winner = $this->pickWinner($abTest);
if (!$winner) {
return;
}
$abTest->declareWinner($winner);
}
private function pickWinner(AbTest $abTest): ?AbTestVariant
{
$campaignId = $abTest->campaign_id;
return $abTest->variants
->map(function (AbTestVariant $variant) use ($campaignId, $abTest) {
$sentIds = TrackingLog::where('campaign_id', $campaignId)
->where('ab_test_variant_id', $variant->id)
->where('status', TrackingLog::STATUS_SENT)
->pluck('message_id');
$sent = $sentIds->count();
if ($sent === 0) {
$variant->_rate = 0.0;
return $variant;
}
if ($abTest->winner_criteria === AbTest::CRITERIA_OPEN_RATE) {
$unique = OpenLog::whereIn('message_id', $sentIds)
->distinct('message_id')
->count('message_id');
} else {
$unique = ClickLog::whereIn('message_id', $sentIds)
->distinct('message_id')
->count('message_id');
}
$variant->_rate = $unique / $sent;
return $variant;
})
->sortByDesc('_rate')
->first();
}
}