60 lines
1.8 KiB
PHP
60 lines
1.8 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
/**
|
|
* One component, one day, counted.
|
|
*
|
|
* The row holds sample counts rather than a percentage. A percentage would
|
|
* have to be recomputed and rewritten on every sample, and the first rounding
|
|
* decision taken during the day becomes a permanent part of the record.
|
|
*
|
|
* A day with no row is a day nobody measured. That is drawn as its own thing —
|
|
* never as an operational day, which is the mistake that makes a status page
|
|
* worthless the moment somebody checks it against their own memory.
|
|
*/
|
|
class StatusDay extends Model
|
|
{
|
|
/** @use HasFactory<\Database\Factories\StatusDayFactory> */
|
|
use HasFactory;
|
|
|
|
protected $fillable = ['day', 'component', 'samples', 'operational', 'degraded', 'down', 'unknown'];
|
|
|
|
protected function casts(): array
|
|
{
|
|
return ['day' => 'date'];
|
|
}
|
|
|
|
/**
|
|
* The share of samples in which the component answered, as a percentage.
|
|
*
|
|
* Degraded counts as up: the service was reachable and doing its job less
|
|
* well, and folding it into downtime would report a slow morning as an
|
|
* outage. It still colours the bar — see the view.
|
|
*/
|
|
public function uptimePercent(): ?float
|
|
{
|
|
$measured = $this->samples - $this->unknown;
|
|
|
|
if ($measured <= 0) {
|
|
return null;
|
|
}
|
|
|
|
return round(($this->operational + $this->degraded) / $measured * 100, 3);
|
|
}
|
|
|
|
/** The worst thing seen that day — what the bar is coloured by. */
|
|
public function worst(): string
|
|
{
|
|
return match (true) {
|
|
$this->down > 0 => 'down',
|
|
$this->degraded > 0 => 'degraded',
|
|
$this->operational > 0 => 'operational',
|
|
default => 'unknown',
|
|
};
|
|
}
|
|
}
|