Files
vkv/app/Jobs/UnpairedClassificationBucketJob.php
Zdeněk Burda 41e3ce6f25 Initial commit
2026-01-09 21:26:40 +01:00

148 lines
5.4 KiB
PHP

<?php
namespace App\Jobs;
use App\Enums\QsoErrorCode;
use App\Models\EvaluationRun;
use App\Models\EvaluationRuleSet;
use App\Models\QsoResult;
use App\Models\WorkingQso;
use App\Services\Evaluation\EvaluationCoordinator;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Bus\Batchable;
use Illuminate\Foundation\Queue\Queueable;
use Throwable;
/**
* Job: UnpairedClassificationBucketJob
*
* Účel:
* - Klasifikace nenapárovaných QSO v bucketu band_id + rcall_norm.
* - Udržuje kratší dobu běhu jednoho jobu.
*/
class UnpairedClassificationBucketJob implements ShouldQueue
{
use Batchable;
use Queueable;
public int $tries = 2;
public array $backoff = [60];
protected ?int $bandId;
protected string $rcallNorm;
public function __construct(
protected int $evaluationRunId,
?int $bandId,
string $rcallNorm
) {
$this->bandId = $bandId;
$this->rcallNorm = $rcallNorm;
}
public function handle(): void
{
$run = EvaluationRun::find($this->evaluationRunId);
if (! $run || $run->isCanceled()) {
return;
}
$coordinator = new EvaluationCoordinator();
try {
$ruleSet = EvaluationRuleSet::find($run->rule_set_id);
if (! $ruleSet) {
$coordinator->eventError($run, 'Unpaired klasifikace nelze spustit: chybí ruleset.', [
'step' => 'unpaired_classification',
]);
return;
}
$coordinator->eventInfo($run, 'Unpaired bucket: krok spuštěn.', [
'step' => 'unpaired_classification',
'round_id' => $run->round_id,
'band_id' => $this->bandId,
'rcall_norm' => $this->rcallNorm,
]);
$workingQuery = WorkingQso::where('evaluation_run_id', $run->id)
->where('rcall_norm', $this->rcallNorm);
if ($this->bandId !== null) {
$workingQuery->where('band_id', $this->bandId);
} else {
$workingQuery->whereNull('band_id');
}
$logQsoIds = $workingQuery->pluck('log_qso_id')->all();
if (! $logQsoIds) {
return;
}
QsoResult::where('evaluation_run_id', $run->id)
->whereNull('matched_log_qso_id')
->whereIn('log_qso_id', $logQsoIds)
->chunkById(500, function ($results) use ($run, $ruleSet) {
foreach ($results as $result) {
$wqso = WorkingQso::where('evaluation_run_id', $run->id)
->where('log_qso_id', $result->log_qso_id)
->first();
if (! $wqso) {
continue;
}
$hasCounterpartLog = false;
if ($wqso->band_id && $wqso->rcall_norm) {
$hasCounterpartLog = WorkingQso::where('evaluation_run_id', $run->id)
->where('band_id', $wqso->band_id)
->where('call_norm', $wqso->rcall_norm)
->exists();
}
if ($hasCounterpartLog) {
$result->error_code = QsoErrorCode::NOT_IN_COUNTERPART_LOG;
$result->is_nil = true;
} else {
$isUnique = false;
if ($ruleSet->uniqueQsoEnabled() && $wqso->rcall_norm) {
$uniqueQuery = WorkingQso::where('evaluation_run_id', $run->id)
->where('rcall_norm', $wqso->rcall_norm);
if ($wqso->band_id) {
$uniqueQuery->where('band_id', $wqso->band_id);
}
$count = $uniqueQuery->count();
$isUnique = $count === 1;
}
if ($isUnique) {
$result->error_code = QsoErrorCode::UNIQUE;
$result->is_nil = false;
} else {
$result->error_code = QsoErrorCode::NO_COUNTERPART_LOG;
$result->is_nil = true;
}
}
$result->is_valid = false;
$result->error_side = 'NONE';
$result->save();
}
});
EvaluationRun::where('id', $run->id)->increment('progress_done');
$coordinator->eventInfo($run, 'Unpaired bucket: krok dokončen.', [
'step' => 'unpaired_classification',
'round_id' => $run->round_id,
'band_id' => $this->bandId,
'rcall_norm' => $this->rcallNorm,
]);
} catch (Throwable $e) {
$coordinator->eventError($run, 'Unpaired bucket: krok selhal.', [
'step' => 'unpaired_classification',
'round_id' => $run->round_id,
'band_id' => $this->bandId,
'rcall_norm' => $this->rcallNorm,
'error' => $e->getMessage(),
]);
throw $e;
}
}
}