%PDF- %PDF-
| Direktori : /www/loslex/demo/app/Models/ |
| Current File : //www/loslex/demo/app/Models/Contest.php |
<?php
namespace App\Models;
use App\Enums\ContestCategoryEnum;
use App\Enums\ContestLevelEnum;
use App\Enums\ContestPublicationStatusEnum;
use App\Enums\ContestRegistrationEnum;
use App\Models\ContestCategory;
use App\Models\ContestDivision;
use App\Models\ContestLevel;
use App\Models\OrganizerGroup;
use App\Models\Range;
use App\Models\Registration;
use App\Models\User;
use App\Workers\AccountNote;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
class Contest extends Model
{
use HasFactory;
protected $fillable = [
'published',
'contestname',
'range_id',
'organizer_group_id',
'date',
'contest_category_id',
'contest_level_id',
'registration_start',
'registration_end',
'registration_type',
'director_id',
'rangemaster_id',
'instructions',
];
protected $casts = [
'settings' => 'array',
'date' => 'date',
'registration_type' => ContestRegistrationEnum::class,
'published' => ContestPublicationStatusEnum::class,
];
protected $locale;
protected $now;
public function __construct() {
parent::__construct();
$this->locale = App::currentLocale();
$this->now = Carbon::now()->locale($this->locale);
}
/** Fill request values into JSON Settings field */
public function fillSettings(Request $request) {
$fields = array(
"presentation_start", "presentation_end",
"contest_start", "contest_end",
"stages", "squads", "capacity", "distribution",
"feelex", "feeothers", "protestdeposit",
"accountnumber","accvarsym", "accnote", "acccondition",
"calibrationwpn","caliberlimit",
"secretstages", "flashlight","canteen","toilet",
"squadshootstyle",
"offsiteregurl", "organizeremail",
"restrictionstext","legend", "partners"
);
$trimfields = array("presentation_start", "presentation_end", "contest_start", "contest_end");
$data = array();
foreach ($fields as $key) {
if (empty($request->input($key))) { continue; }
switch($key) {
case "restrictionstext":
case "legend":
case "partners":
$data[$key] = $this->normalizeInstructions($request->input($key));
break;
default:
$data[$key] = $request->input($key);
break;
}
}
// if squad shoots together no need to store separate times.
// OTOH we need to store the null for [0] field to avoid array shifting when reading to use in contest
if ($request->squadshootstyle == 'together') {
foreach ($trimfields as $key) { $data[$key] = array_splice($data[$key], 0, 1); }
}
$this->settings = $data;
}
/** Normalize freetext instructions text - trims whitespaces, converts all linebreaks to \n and reduces extra newlines */
protected function normalizeInstructions(String $instructions) : String
{
$result = trim($instructions); // trim starting and ending whitespaces
$result = str_replace(array("\r\n", "\r", "\n"), "\n", $result); // replace all wariants to single \n
$result = preg_replace("/\n{3,}/", "\n\n", $result); // avoid extra whitespacing of the of the content
return $result;
}
public function instructions(): Attribute
{
return Attribute::make(
get: fn ($value) => $value,
set: fn ($value) => $this->normalizeInstructions($value),
);
}
public function partnersArr(): Attribute
{
return Attribute::make(
get: function () {
$raw = $this->settings['partners'] ?? null;
if (is_null($raw)) { return null; }
$partnerlines = preg_split("/[\n;]/", $raw);
foreach ($partnerlines as $partner) {
if (preg_match("/\[(.*?)\]\((.*?)\)/", $partner, $matches)) {
$partners[] = array('name' => $matches[1], 'url' => $matches[2]);
} else {
$partners[] = array('name' => $partner);
}
}
return $partners;
},
);
}
/* ========== ========== Getters for reg_start/end ========== ========== */
public function registrationStart(): Attribute { return Attribute::make(get: fn($value) => is_null($value) ? null : new Carbon($value) ); }
public function registrationEnd(): Attribute { return Attribute::make(get: fn($value) => is_null($value) ? null : new Carbon($value) ); }
/* ========== ========== Custom settings attributes ========== ========== */
public function acccondition(): Attribute { return Attribute::make(get: fn () => $this->settings['acccondition'] ?? null); }
public function accnote(): Attribute { return Attribute::make(get: fn () => $this->settings['accnote'] ?? null); }
public function accountnumber(): Attribute { return Attribute::make(get: fn () => $this->settings['accountnumber'] ?? null); }
public function accvarsym(): Attribute { return Attribute::make(get: fn () => $this->settings['accvarsym'] ?? null); }
public function caliberlimit(): Attribute { return Attribute::make(get: fn () => $this->settings['caliberlimit'] ?? null); }
public function calibrationwpn(): Attribute { return Attribute::make(get: fn () => $this->settings['calibrationwpn'] ?? null); }
public function canteen(): Attribute { return Attribute::make(get: fn () => $this->settings['canteen'] ?? 0); }
public function capacity(): Attribute { return Attribute::make(get: fn () => $this->settings['capacity'] ?? 0); }
public function contestEnd(): Attribute { return Attribute::make(get: fn () => $this->settings['contest_end'] ?? null); }
public function contestStart(): Attribute { return Attribute::make(get: fn () => $this->settings['contest_start'] ?? null); }
public function distribution(): Attribute { return Attribute::make(get: fn () => $this->settings['distribution'] ?? null); }
public function feelex(): Attribute { return Attribute::make(get: fn () => $this->settings['feelex'] ?? 0); }
public function feeothers(): Attribute { return Attribute::make(get: fn () => $this->settings['feeothers'] ?? 0); }
public function flashlight(): Attribute { return Attribute::make(get: fn () => $this->settings['flashlight'] ?? 0); }
public function legend(): Attribute { return Attribute::make(get: fn () => $this->settings['legend'] ?? null); }
public function offsiteregurl(): Attribute { return Attribute::make(get: fn () => $this->settings['offsiteregurl'] ?? ""); }
public function organizerEmail(): Attribute { return Attribute::make(get: fn () => $this->settings['organizeremail'] ?? null); }
public function partners(): Attribute { return Attribute::make(get: fn () => $this->settings['partners'] ?? null); }
public function presentationEnd(): Attribute { return Attribute::make(get: fn () => $this->settings['presentation_end'] ?? null); }
public function presentationStart(): Attribute { return Attribute::make(get: fn () => $this->settings['presentation_start'] ?? null); }
public function protestdeposit(): Attribute { return Attribute::make(get: fn () => $this->settings['protestdeposit'] ?? 0); }
public function restrictionstext(): Attribute { return Attribute::make(get: fn () => $this->settings['restrictionstext'] ?? null); }
public function secretstages(): Attribute { return Attribute::make(get: fn () => $this->settings['secretstages'] ?? 0); }
public function squads(): Attribute { return Attribute::make(get: fn () => $this->settings['squads'] ?? 0); }
public function squadShootStyle(): Attribute { return Attribute::make(get: fn () => $this->settings['squadshootstyle'] ?? null); }
public function stages(): Attribute { return Attribute::make(get: fn () => $this->settings['stages'] ?? 0); }
public function toilet(): Attribute { return Attribute::make(get: fn () => $this->settings['toilet'] ?? 0); }
public function paymentnote(): Attribute
{
return Attribute::make(
get: function() {
$paymentnote = new AccountNote($this->accnote, Auth::user());
return $paymentnote->getPaymentNote();
}
);
}
/* ========== ========== Other helpers ========== ========== */
/** Is registration active flag */
public function isRegActive(): Attribute
{
$result = $this->now->gte($this->registration_start) && $this->now->lte($this->registration_end);
return Attribute::make(
get: fn() => $result,
);
}
/** Waiting for registration to begin flag */
public function isBeforeReg(): Attribute
{
$regStart = Carbon::parse($this->registration_start)->locale($this->locale);
return Attribute::make(
get: fn () => $this->now->lt($regStart),
);
}
/** Registration ended flag */
public function isAfterReg(): Attribute
{
$regEnd = Carbon::parse($this->registration_end)->locale($this->locale);
return Attribute::make(
get: fn () => $this->now->gt($regEnd),
);
}
/** Returns true if "today" is after contest date */
public function isAfterContest(): Attribute
{
$today = Carbon::today()->locale($this->locale);
return Attribute::make(
get: fn() => $this->date->lt($today)
);
}
/** Contest date flag */
public function isContestDay(): Attribute
{
$today = Carbon::today()->locale($this->locale);
return Attribute::make(
get: fn() => $this->date->eq($today)
);
}
/** returns number of days related to "today" */
public function dayShift(): Attribute
{
$today = Carbon::today()->locale($this->locale);
return Attribute::make(
get: fn() => $today->diffInDays($this->date, false)
);
}
// /* Get the localized date formats */
public function dateDay(): Attribute
{
$this->date->settings(['formatFunction' => 'translatedFormat']);
return Attribute::make(get: fn () => $this->date->format('d'));
}
public function dateMth(): Attribute
{
$dateval = Carbon::parse($this->date)->locale($this->locale);
$dateval->settings(['formatFunction' => 'translatedFormat']);
return Attribute::make(get: fn () => $dateval->format('M'));
}
public function dateDow(): Attribute
{
$dateval = Carbon::parse($this->date)->locale($this->locale);
$dateval->settings(['formatFunction' => 'translatedFormat']);
return Attribute::make(get: fn () => $dateval->format('l'));
}
/** Helper function to equally distribute capacity of the contest between squads */
public function squadSize(): Attribute
{
return Attribute::make(
get: function() {
$base = intdiv($this->capacity, $this->squads);
$arr = array_fill(1, $this->squads, $base);
$reduction = array_reduce($arr, function($res, $a) { return $res + $a; }, 0);
if ($reduction == $this->capacity) { return $arr; }
if ($this->distribution == "asc") {
for ($i = 1; $i <= $this->squads; $i++) {
$arr[$i]++;
$reduction = array_reduce($arr, function($res, $a) { return $res + $a; }, 0);
if ($reduction == $this->capacity) { return $arr; }
}
}
else {
for ($i = $this->squads; $i > 0; $i--) {
$arr[$i]++;
$reduction = array_reduce($arr, function($res, $a) { return $res + $a; }, 0);
if ($reduction == $this->capacity) { return $arr; }
}
}
}
);
}
/** Returns collection of divisions and count of registered */
public function divisionCounts(): Attribute
{
return Attribute::make(
get: function() {
return $this->registrations->groupBy(function(Registration $item) {
return $item->division?->bgdivision;
})->map->count();
}
);
}
/** Identifies whether the contest is LOSÃk */
public function isLosik(): bool
{
return $this->contest_level_id == ContestLevelEnum::LOSIK->value;
}
/** identifies whether the contest is classification for Voluntary State Reserves */
public function isSzsClassification(): bool
{
return $this->contest_level_id == ContestLevelEnum::SZSCLASSIFICATION->value;
}
public function getCacheKeyPrefix(string $use): string
{
return "contest/" . $this->id . "/" . $use . "/";
}
/* ========== ========== Foreign keys ========== ========== */
/** Get the level of contest/event is placed
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo */
public function contest_level(): BelongsTo
{
return $this->belongsTo(ContestLevel::class);
}
/** Get the category of contest
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo */
public function contest_category(): BelongsTo
{
return $this->belongsTo(ContestCategory::class);
}
/** Get the range where contest is placed
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo */
public function range(): BelongsTo
{
return $this->belongsTo(Range::class);
}
/** Get the organizer that owns the Contest
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo */
public function organizer_group(): BelongsTo
{
return $this->belongsTo(OrganizerGroup::class);
}
/** Get the director of the Contest
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo */
public function director(): BelongsTo
{
return $this->belongsTo(User::class);
}
/** Get the range master of the Contest
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo */
public function rangemaster(): BelongsTo
{
return $this->belongsTo(User::class);
}
/** Registrations for the contest
* @return \Illuminate\Database\Eloquent\Relations\HasMany */
public function registrations() : HasMany
{
return $this->hasMany(Registration::class);
}
}