Initial commit

This commit is contained in:
Zdeněk Burda
2026-01-09 21:26:40 +01:00
parent e83aec6dca
commit 41e3ce6f25
404 changed files with 61250 additions and 28 deletions

View File

@@ -0,0 +1,239 @@
<?php
namespace App\Http\Controllers;
use App\Models\LogResult;
use App\Models\EvaluationRun;
use App\Models\Round;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Str;
class LogResultController extends BaseController
{
use AuthorizesRequests, ValidatesRequests;
public function __construct()
{
// zápisové operace jen pro přihlášené
$this->middleware('auth:sanctum')->only(['store', 'update', 'destroy']);
}
/**
* Seznam výsledků logů filtrování podle evaluation_run_id,
* log_id, band_id, category_id, status.
*/
public function index(Request $request): JsonResponse
{
$perPage = (int) $request->get('per_page', 100);
$statusParam = $request->get('status');
$isClaimedRequest = $statusParam === 'CLAIMED';
$query = LogResult::query()
->with([
'evaluationRun.ruleSet:id,sixhr_ranking_mode',
'log',
'band:id,name,order',
'category:id,name,order',
'powerCategory:id,name,order',
]);
if ($request->filled('evaluation_run_id')) {
$query->where('evaluation_run_id', (int) $request->get('evaluation_run_id'));
}
if ($request->filled('round_id')) {
$roundId = (int) $request->get('round_id');
$query->whereHas('log', function ($q) use ($roundId) {
$q->where('round_id', $roundId);
});
if (! $request->filled('evaluation_run_id') && $request->filled('result_type')) {
$round = Round::find($roundId);
$resultType = strtoupper((string) $request->get('result_type'));
$selectedRunId = null;
if ($round) {
if ($resultType === 'FINAL') {
$selectedRunId = $round->official_evaluation_run_id;
} elseif ($resultType === 'PRELIMINARY') {
$selectedRunId = $round->preliminary_evaluation_run_id;
} elseif ($resultType === 'TEST') {
$selectedRunId = $round->test_evaluation_run_id;
} elseif ($resultType === 'AUTO') {
$selectedRunId = $round->official_evaluation_run_id
?? $round->preliminary_evaluation_run_id;
}
}
if ($selectedRunId) {
$query->where('evaluation_run_id', $selectedRunId);
} else {
$query->whereRaw('1=0');
}
}
if (! $request->filled('evaluation_run_id') && $isClaimedRequest) {
$latestClaimedRunId = EvaluationRun::where('round_id', $roundId)
->where('rules_version', 'CLAIMED')
->orderByDesc('id')
->value('id');
if ($latestClaimedRunId) {
$query->where('evaluation_run_id', $latestClaimedRunId);
}
}
}
if ($request->filled('log_id')) {
$query->where('log_id', (int) $request->get('log_id'));
}
if ($request->filled('band_id')) {
$query->where('band_id', (int) $request->get('band_id'));
}
if ($request->filled('category_id')) {
$query->where('category_id', (int) $request->get('category_id'));
}
if ($request->filled('status') && ! $isClaimedRequest) {
$query->where('status', $statusParam);
}
if ($request->boolean('only_ok', false)) {
$pcallExpr = "UPPER(REPLACE(TRIM(pcall), ' ', ''))";
$query->whereHas('log', function ($q) use ($pcallExpr) {
$q->where(function ($sub) use ($pcallExpr) {
$sub->whereRaw("{$pcallExpr} LIKE ?", ['OK%'])
->orWhereRaw("{$pcallExpr} LIKE ?", ['OL%'])
->orWhereRaw("{$pcallExpr} LIKE ?", ['%/OK%'])
->orWhereRaw("{$pcallExpr} LIKE ?", ['%/OL%']);
});
});
}
// implicitně řadit podle oficiálního skóre
$items = $query
->orderByDesc('official_score')
->paginate($perPage);
return response()->json($items);
}
/**
* Vytvoření záznamu výsledku logu.
* Typicky voláno vyhodnocovačem, ne přímo z UI.
*/
public function store(Request $request): JsonResponse
{
$this->authorize('create', LogResult::class);
$data = $this->validateData($request);
$result = LogResult::create($data);
$result->load([
'evaluationRun.ruleSet:id,sixhr_ranking_mode',
'log',
'band:id,name,order',
'category:id,name,order',
'powerCategory:id,name,order',
]);
return response()->json($result, 201);
}
/**
* Detail jednoho výsledku.
*/
public function show(LogResult $logResult): JsonResponse
{
$logResult->load([
'evaluationRun.ruleSet:id,sixhr_ranking_mode',
'log',
'band:id,name,order',
'category:id,name,order',
'powerCategory:id,name,order',
]);
return response()->json($logResult);
}
/**
* Aktualizace výsledku (partial).
* Typicky pro ruční korekci statutu / poznámky.
*/
public function update(Request $request, LogResult $logResult): JsonResponse
{
$this->authorize('update', $logResult);
$data = $this->validateData($request, partial: true);
$logResult->fill($data);
$logResult->save();
$logResult->load([
'evaluationRun.ruleSet:id,sixhr_ranking_mode',
'log',
'band:id,name,order',
'category:id,name,order',
'powerCategory:id,name,order',
]);
return response()->json($logResult);
}
/**
* Smazání výsledku.
*/
public function destroy(LogResult $logResult): JsonResponse
{
$this->authorize('delete', $logResult);
$logResult->delete();
return response()->json(null, 204);
}
/**
* Validace pro store / update.
*/
protected function validateData(Request $request, bool $partial = false): array
{
$required = $partial ? 'sometimes' : 'required';
return $request->validate([
'evaluation_run_id' => [$required, 'integer', 'exists:evaluation_runs,id'],
'log_id' => [$required, 'integer', 'exists:logs,id'],
'band_id' => ['sometimes', 'nullable', 'integer', 'exists:bands,id'],
'category_id' => ['sometimes', 'nullable', 'integer', 'exists:categories,id'],
'power_category_id' => ['sometimes', 'nullable', 'integer', 'exists:power_categories,id'],
'claimed_qso_count' => ['sometimes', 'nullable', 'integer', 'min:0'],
'claimed_score' => ['sometimes', 'nullable', 'integer', 'min:0'],
'valid_qso_count' => ['sometimes', 'integer', 'min:0'],
'dupe_qso_count' => ['sometimes', 'integer', 'min:0'],
'busted_qso_count' => ['sometimes', 'integer', 'min:0'],
'other_error_qso_count' => ['sometimes', 'integer', 'min:0'],
'total_qso_count' => ['sometimes', 'integer', 'min:0'],
'discarded_qso_count' => ['sometimes', 'integer', 'min:0'],
'discarded_points' => ['sometimes', 'integer'],
'discarded_qso_percent' => ['sometimes', 'numeric', 'min:0'],
'unique_qso_count' => ['sometimes', 'integer', 'min:0'],
'official_score' => ['sometimes', 'integer'],
'penalty_score' => ['sometimes', 'integer'],
'base_score' => ['sometimes', 'integer'],
'multiplier_count' => ['sometimes', 'integer', 'min:0'],
'multiplier_score' => ['sometimes', 'integer'],
'score_per_qso' => ['sometimes', 'numeric', 'min:0'],
'rank_overall' => ['sometimes', 'nullable', 'integer', 'min:1'],
'rank_in_category' => ['sometimes', 'nullable', 'integer', 'min:1'],
'status' => ['sometimes', 'string', 'max:20'],
'status_reason' => ['sometimes', 'nullable', 'string'],
]);
}
}