MIF_E31231313/Website/s_gizi/app/Http/Controllers/Api/GiziController.php

261 lines
12 KiB
PHP

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Api\Concerns\ResolvesApiUser;
use App\Http\Controllers\Controller;
use App\Models\Child;
use App\Models\GrowthRecord;
use App\Models\Measurement;
use App\Services\GiziService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class GiziController extends Controller
{
use ResolvesApiUser;
// Kode ini digunakan untuk memasukkan GiziService ke dalam controller API.
public function __construct(private readonly GiziService $giziService)
{
}
// Kode ini digunakan untuk menerima pengukuran, menghitung status gizi, dan menyimpan hasilnya.
public function hitung(Request $request): JsonResponse
{
// PRESENTASI TA: Endpoint hitung menghubungkan aplikasi, GiziService, dan penyimpanan riwayat pengukuran.
$user = $this->userFromToken($request);
if (!$user) return $this->unauthenticated();
$validated = $request->validate([
'child_id' => ['required', 'integer', 'exists:children,id'],
'tanggal_ukur' => ['required', 'date'],
'berat_badan' => ['required', 'numeric', 'gt:0', 'max:80'],
'tinggi_badan' => ['required', 'numeric', 'gt:0', 'min:30', 'max:130'],
'cara_ukur' => ['required', 'in:standing,lying'],
'is_anomaly' => ['nullable', 'boolean'],
'validation_status' => ['nullable', 'in:valid,perlu_ukur_ulang'],
'validation_note' => ['nullable', 'string', 'max:1000'],
'monitoring_status' => ['nullable', 'in:normal,perlu_dipantau'],
'is_confirmed_by_parent' => ['nullable', 'boolean'],
]);
$child = Child::query()
->where('user_id', $user->id)
->findOrFail($validated['child_id']);
$result = $this->giziService->hitung([
'jenis_kelamin' => $child->jenis_kelamin,
'tanggal_lahir' => $child->tanggal_lahir->toDateString(),
'tanggal_ukur' => $validated['tanggal_ukur'],
'berat_badan' => $validated['berat_badan'],
'tinggi_badan' => $validated['tinggi_badan'],
'cara_ukur' => $validated['cara_ukur'],
]);
if (isset($result['error'])) {
return response()->json($result, 422);
}
$hasExtremeZScore = collect($result['zscore'] ?? [])
->filter(fn ($value) => is_numeric($value))
->contains(fn ($value) => (float) $value < -6 || (float) $value > 6);
$validationStatus = (string) ($validated['validation_status'] ?? 'valid');
if ($hasExtremeZScore) {
$validationStatus = 'perlu_ukur_ulang';
}
$monitoringStatus = (string) ($validated['monitoring_status'] ?? 'normal');
$isAnomaly = (bool) ($validated['is_anomaly'] ?? false) || $hasExtremeZScore || $validationStatus === 'perlu_ukur_ulang';
$dataStatus = $validationStatus === 'perlu_ukur_ulang' ? 'perlu_ukur_ulang' : 'normal';
$validationNote = trim((string) ($validated['validation_note'] ?? ''));
if ($validationNote === '' && $hasExtremeZScore) {
$validationNote = 'Hasil Z-score berada di luar batas normal WHO dan perlu dicek ulang.';
}
$measurement = Measurement::query()->create([
'child_id' => $child->id,
'berat' => $validated['berat_badan'],
'tinggi' => $validated['tinggi_badan'],
'tanggal_ukur' => $validated['tanggal_ukur'],
'cara_ukur' => $validated['cara_ukur'],
'umur_bulan' => $result['identitas']['umur_bulan'],
'z_bbu' => $result['zscore']['bbu'],
'z_tbu' => $result['zscore']['tbu'],
'z_bbtb' => $result['zscore']['bbtb'],
'kategori' => $result['kategori'],
'status_gabungan' => $result['status_gabungan'],
'is_anomaly' => $isAnomaly,
'data_status' => $dataStatus,
'validation_status' => $validationStatus,
'validation_note' => $validationNote,
'monitoring_status' => $monitoringStatus,
'is_confirmed_by_parent' => (bool) ($validated['is_confirmed_by_parent'] ?? false),
'created_at' => now(),
]);
GrowthRecord::query()->create([
'child_id' => $child->id,
'tinggi_badan' => $validated['tinggi_badan'],
'berat_badan' => $validated['berat_badan'],
'umur_dalam_bulan' => $result['identitas']['umur_bulan'],
'z_score' => $this->primaryZScore($result['status_gabungan'], $result['zscore']),
'status_gizi' => $result['status_gabungan'],
'created_at' => now(),
]);
return response()->json([
...$result,
'measurement' => [
'id' => $measurement->id,
'child_id' => $child->id,
'child_name' => $child->nama_anak ?? $child->nama,
'tanggal_ukur' => $measurement->tanggal_ukur->toDateString(),
'cara_ukur' => $measurement->cara_ukur,
'is_anomaly' => $measurement->is_anomaly,
'data_status' => $measurement->data_status,
'validation_status' => $measurement->validation_status ?? 'valid',
'validation_note' => $measurement->validation_note ?? '',
'monitoring_status' => $measurement->monitoring_status ?? 'normal',
'is_confirmed_by_parent' => (bool) $measurement->is_confirmed_by_parent,
],
]);
}
// Kode ini digunakan untuk menampilkan riwayat pengukuran milik anak pengguna.
public function riwayat(Request $request, int $childId): JsonResponse
{
$user = $this->userFromToken($request);
if (!$user) return $this->unauthenticated();
$child = Child::query()->where('user_id', $user->id)->findOrFail($childId);
return response()->json([
'child' => [
'id' => $child->id,
'nama' => $child->nama_anak ?? $child->nama,
'nama_anak' => $child->nama_anak ?? $child->nama,
'tanggal_lahir' => $child->tanggal_lahir->toDateString(),
'jenis_kelamin' => $child->jenis_kelamin,
],
'riwayat' => $child->measurements()
->orderBy('tanggal_ukur')
->orderBy('id')
->get()
->map(fn (Measurement $item) => [
'id' => $item->id,
'berat' => $item->berat,
'tinggi' => $item->tinggi,
'tanggal_ukur' => $item->tanggal_ukur->toDateString(),
'cara_ukur' => $item->cara_ukur,
'umur_bulan' => $item->umur_bulan,
'status_gabungan' => $item->status_gabungan,
'is_anomaly' => (bool) $item->is_anomaly,
'data_status' => $item->data_status ?? 'normal',
'validation_status' => $item->validation_status ?? 'valid',
'validation_note' => $item->validation_note ?? '',
'monitoring_status' => $item->monitoring_status ?? 'normal',
'is_confirmed_by_parent' => (bool) $item->is_confirmed_by_parent,
'kategori' => $item->kategori,
'z_score' => [
'bbu' => $item->z_bbu,
'tbu' => $item->z_tbu,
'bbtb' => $item->z_bbtb,
],
]),
]);
}
// Kode ini digunakan untuk mengonfirmasi validitas hasil pengukuran oleh orang tua.
public function confirm(Request $request, int $measurementId): JsonResponse
{
$user = $this->userFromToken($request);
if (!$user) return $this->unauthenticated();
$validated = $request->validate([
'validation_status' => ['nullable', 'in:valid,perlu_ukur_ulang'],
'monitoring_status' => ['nullable', 'in:normal,perlu_dipantau'],
'validation_note' => ['nullable', 'string', 'max:1000'],
]);
$measurement = Measurement::query()
->whereHas('child', fn ($query) => $query->where('user_id', $user->id))
->findOrFail($measurementId);
$measurement->update([
'validation_status' => $validated['validation_status'] ?? $measurement->validation_status ?? 'valid',
'monitoring_status' => $validated['monitoring_status'] ?? $measurement->monitoring_status ?? 'normal',
'validation_note' => $validated['validation_note'] ?? $measurement->validation_note,
'is_confirmed_by_parent' => true,
]);
return response()->json([
'message' => 'Data pengukuran berhasil dikonfirmasi.',
'data' => $this->measurementResultPayload($measurement->fresh('child')),
]);
}
// Kode ini digunakan untuk menampilkan detail hasil suatu pengukuran.
public function result(Request $request, int $measurementId): JsonResponse
{
$user = $this->userFromToken($request);
if (!$user) return $this->unauthenticated();
$measurement = Measurement::query()
->with('child')
->whereHas('child', fn ($query) => $query->where('user_id', $user->id))
->findOrFail($measurementId);
return response()->json($this->measurementResultPayload($measurement));
}
// Kode ini digunakan untuk memilih Z-Score utama yang mewakili status gabungan.
private function primaryZScore(string $status, array $scores): float
{
$status = strtolower($status);
if (str_contains($status, 'pendek')) {
return (float) $scores['tbu'];
}
if (str_contains($status, 'gizi buruk') || str_contains($status, 'gizi kurang') || str_contains($status, 'obesitas') || str_contains($status, 'gizi lebih') || str_contains($status, 'risiko berat badan lebih')) {
return (float) $scores['bbtb'];
}
return (float) $scores['bbu'];
}
// Kode ini digunakan untuk membentuk struktur data hasil pengukuran bagi respons API.
private function measurementResultPayload(Measurement $measurement): array
{
return [
'identitas' => [
'umur_bulan' => $measurement->umur_bulan,
'umur_hari' => null,
'jenis_kelamin' => $measurement->child?->jenis_kelamin ?? '-',
'cara_ukur' => $measurement->cara_ukur ?? '-',
'standar_bbtb' => 'BB/TB',
],
'zscore' => [
'bbu' => $measurement->z_bbu,
'tbu' => $measurement->z_tbu,
'bbtb' => $measurement->z_bbtb,
],
'kategori' => is_array($measurement->kategori) ? $measurement->kategori : [],
'status_gabungan' => $measurement->status_gabungan,
'rekomendasi' => [],
'measurement' => [
'id' => $measurement->id,
'child_id' => $measurement->child_id,
'child_name' => $measurement->child?->nama_anak ?? $measurement->child?->nama,
'tanggal_ukur' => $measurement->tanggal_ukur?->toDateString(),
'cara_ukur' => $measurement->cara_ukur,
'is_anomaly' => (bool) $measurement->is_anomaly,
'data_status' => $measurement->data_status ?? 'normal',
'validation_status' => $measurement->validation_status ?? 'valid',
'validation_note' => $measurement->validation_note ?? '',
'monitoring_status' => $measurement->monitoring_status ?? 'normal',
'is_confirmed_by_parent' => (bool) $measurement->is_confirmed_by_parent,
],
];
}
}