'date', 'disk_used_bytes' => 'integer', 'disk_total_bytes' => 'integer', 'rx_bytes' => 'integer', 'tx_bytes' => 'integer', 'checks_total' => 'integer', 'checks_ok' => 'integer', ]; } public function instance(): BelongsTo { return $this->belongsTo(Instance::class); } /** * The last $days days, oldest first, with gaps left as gaps. * * A missing day is not a zero. Filling it would draw a cliff into the chart * on every day the sampler could not reach the host, and the customer would * read an outage that never happened. * * @return Collection */ public static function series(Instance $instance, int $days = 14): Collection { return self::query() ->where('instance_id', $instance->id) ->where('day', '>=', Carbon::today()->subDays($days - 1)) ->orderBy('day') ->get(); } /** * Availability over the window, as a percentage, or null. * * Null when nothing was ever checked. Reporting an unmonitored instance as * 100 % is the single most dishonest number this application could print — * it is the figure a customer would quote to their own auditor. */ public static function availability(Instance $instance, int $days = 30): ?float { $row = self::query() ->where('instance_id', $instance->id) ->where('day', '>=', Carbon::today()->subDays($days - 1)) ->selectRaw('SUM(checks_total) as total, SUM(checks_ok) as ok') ->first(); $total = (int) ($row->total ?? 0); return $total === 0 ? null : round((int) $row->ok / $total * 100, 2); } /** * Daily availability for the trend, oldest first. * * Days without a single check are skipped rather than drawn at zero: our * monitoring being down is not the customer's cloud being down. * * @return array */ public static function availabilitySeries(Instance $instance, int $days = 30): array { return self::query() ->where('instance_id', $instance->id) ->where('day', '>=', Carbon::today()->subDays($days - 1)) ->where('checks_total', '>', 0) ->orderBy('day') ->get() ->map(fn (self $m) => round($m->checks_ok / max(1, $m->checks_total) * 100, 2)) ->all(); } /** The most recent day that carries a disk reading, or null. */ public static function latestDisk(Instance $instance): ?self { return self::query() ->where('instance_id', $instance->id) ->whereNotNull('disk_used_bytes') ->orderByDesc('day') ->first(); } }