Initial WebGIS portal project
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
function ensurePendudukFeatureSchema(PDO $pdo): void
|
||||
{
|
||||
$columns = [
|
||||
'kategori_kebutuhan' => "ALTER TABLE penduduk_miskin ADD COLUMN kategori_kebutuhan VARCHAR(100) NULL AFTER status_bantuan",
|
||||
'deskripsi_kebutuhan' => "ALTER TABLE penduduk_miskin ADD COLUMN deskripsi_kebutuhan TEXT NULL AFTER kategori_kebutuhan",
|
||||
'tindak_lanjut' => "ALTER TABLE penduduk_miskin ADD COLUMN tindak_lanjut VARCHAR(60) NOT NULL DEFAULT 'Belum disurvei' AFTER deskripsi_kebutuhan",
|
||||
'kondisi_rumah' => "ALTER TABLE penduduk_miskin ADD COLUMN kondisi_rumah VARCHAR(80) NULL AFTER tindak_lanjut",
|
||||
'penghasilan' => "ALTER TABLE penduduk_miskin ADD COLUMN penghasilan INT NULL AFTER kondisi_rumah",
|
||||
'catatan_survei' => "ALTER TABLE penduduk_miskin ADD COLUMN catatan_survei TEXT NULL AFTER penghasilan",
|
||||
'bukti_file' => "ALTER TABLE penduduk_miskin ADD COLUMN bukti_file VARCHAR(255) NULL AFTER catatan_survei",
|
||||
'skor_prioritas' => "ALTER TABLE penduduk_miskin ADD COLUMN skor_prioritas INT NOT NULL DEFAULT 0 AFTER bukti_file",
|
||||
];
|
||||
|
||||
foreach ($columns as $column => $sql) {
|
||||
$stmt = $pdo->query("SHOW COLUMNS FROM penduduk_miskin LIKE " . $pdo->quote($column));
|
||||
if ($stmt->fetch() === false) {
|
||||
$pdo->exec($sql);
|
||||
}
|
||||
}
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE IF NOT EXISTS riwayat_bantuan (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
penduduk_id INT NOT NULL,
|
||||
jenis_bantuan VARCHAR(120) NOT NULL,
|
||||
tanggal_bantuan DATE NOT NULL,
|
||||
nilai_bantuan VARCHAR(80) NULL,
|
||||
pemberi_bantuan VARCHAR(120) NULL,
|
||||
catatan TEXT NULL,
|
||||
bukti_file VARCHAR(255) NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (penduduk_id) REFERENCES penduduk_miskin(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
");
|
||||
}
|
||||
|
||||
function normalizeDateOrNull(?string $value): ?string
|
||||
{
|
||||
$value = trim((string)$value);
|
||||
return $value === '' || $value === '0000-00-00' ? null : $value;
|
||||
}
|
||||
|
||||
function normalizeNonNegativeIntOrNull(mixed $value, string $label): ?int
|
||||
{
|
||||
$value = trim((string)$value);
|
||||
if ($value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!is_numeric($value) || (float)$value < 0) {
|
||||
throw new InvalidArgumentException($label . ' tidak boleh bernilai negatif.');
|
||||
}
|
||||
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
function calculatePriorityScore(array $penduduk, array $anggota = []): int
|
||||
{
|
||||
$score = 0;
|
||||
$anggotaCount = count($anggota);
|
||||
$penghasilan = isset($penduduk['penghasilan']) ? (int)$penduduk['penghasilan'] : 0;
|
||||
|
||||
if (($penduduk['status_bantuan'] ?? '') === 'Belum dibantu') $score += 30;
|
||||
if (($penduduk['status_bantuan'] ?? '') === 'Menunggu verifikasi') $score += 15;
|
||||
if ((int)($penduduk['is_blank_spot'] ?? 0) === 1) $score += 15;
|
||||
|
||||
$score += min(20, $anggotaCount * 4);
|
||||
|
||||
foreach ($anggota as $a) {
|
||||
$umur = isset($a['umur']) ? (int)$a['umur'] : null;
|
||||
if ($umur !== null && ($umur < 6 || $umur >= 60)) {
|
||||
$score += 5;
|
||||
}
|
||||
}
|
||||
|
||||
if ($penghasilan > 0 && $penghasilan <= 1000000) $score += 15;
|
||||
elseif ($penghasilan > 1000000 && $penghasilan <= 2000000) $score += 8;
|
||||
|
||||
$kondisi = strtolower((string)($penduduk['kondisi_rumah'] ?? ''));
|
||||
if (str_contains($kondisi, 'tidak layak')) $score += 15;
|
||||
elseif (str_contains($kondisi, 'rentan')) $score += 8;
|
||||
|
||||
$kebutuhan = strtolower((string)($penduduk['kategori_kebutuhan'] ?? ''));
|
||||
if (in_array($kebutuhan, ['kesehatan', 'rumah tidak layak', 'bantuan darurat'], true)) $score += 10;
|
||||
|
||||
return min(100, max(0, $score));
|
||||
}
|
||||
|
||||
function getPriorityLabel(int $score): string
|
||||
{
|
||||
if ($score >= 70) return 'Prioritas Tinggi';
|
||||
if ($score >= 40) return 'Prioritas Sedang';
|
||||
return 'Prioritas Rendah';
|
||||
}
|
||||
|
||||
function saveUploadedEvidence(string $fieldName): ?string
|
||||
{
|
||||
if (empty($_FILES[$fieldName]) || !is_array($_FILES[$fieldName])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$file = $_FILES[$fieldName];
|
||||
if (($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (($file['error'] ?? UPLOAD_ERR_OK) !== UPLOAD_ERR_OK) {
|
||||
throw new RuntimeException('Upload bukti gagal.');
|
||||
}
|
||||
|
||||
if (($file['size'] ?? 0) > 3 * 1024 * 1024) {
|
||||
throw new RuntimeException('Ukuran bukti maksimal 3 MB.');
|
||||
}
|
||||
|
||||
$allowed = [
|
||||
'image/jpeg' => 'jpg',
|
||||
'image/png' => 'png',
|
||||
'image/webp' => 'webp',
|
||||
'application/pdf' => 'pdf',
|
||||
];
|
||||
$mime = mime_content_type($file['tmp_name']);
|
||||
|
||||
if (!isset($allowed[$mime])) {
|
||||
throw new RuntimeException('Format bukti harus JPG, PNG, WEBP, atau PDF.');
|
||||
}
|
||||
|
||||
$dir = __DIR__ . '/../uploads/bukti';
|
||||
if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) {
|
||||
throw new RuntimeException('Folder upload tidak bisa dibuat.');
|
||||
}
|
||||
|
||||
$filename = date('YmdHis') . '-' . bin2hex(random_bytes(6)) . '.' . $allowed[$mime];
|
||||
$target = $dir . '/' . $filename;
|
||||
|
||||
if (!move_uploaded_file($file['tmp_name'], $target)) {
|
||||
throw new RuntimeException('Bukti tidak bisa disimpan.');
|
||||
}
|
||||
|
||||
return 'uploads/bukti/' . $filename;
|
||||
}
|
||||
|
||||
function readPendudukInput(): array
|
||||
{
|
||||
if (str_starts_with((string)($_SERVER['CONTENT_TYPE'] ?? ''), 'multipart/form-data')) {
|
||||
$input = $_POST;
|
||||
$input['anggota'] = json_decode((string)($_POST['anggota'] ?? '[]'), true) ?: [];
|
||||
return $input;
|
||||
}
|
||||
|
||||
return json_decode(file_get_contents('php://input'), true) ?: [];
|
||||
}
|
||||
|
||||
function updatePendudukPriority(PDO $pdo, int $pendudukId): void
|
||||
{
|
||||
$stmt = $pdo->prepare("SELECT * FROM penduduk_miskin WHERE id = ?");
|
||||
$stmt->execute([$pendudukId]);
|
||||
$penduduk = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!$penduduk) return;
|
||||
|
||||
$stmtAnggota = $pdo->prepare("SELECT * FROM anggota_keluarga WHERE penduduk_id = ?");
|
||||
$stmtAnggota->execute([$pendudukId]);
|
||||
$anggota = $stmtAnggota->fetchAll(PDO::FETCH_ASSOC);
|
||||
$score = calculatePriorityScore($penduduk, $anggota);
|
||||
|
||||
$update = $pdo->prepare("UPDATE penduduk_miskin SET skor_prioritas = ? WHERE id = ?");
|
||||
$update->execute([$score, $pendudukId]);
|
||||
}
|
||||
|
||||
function refreshAllPriorityScores(PDO $pdo): void
|
||||
{
|
||||
ensurePendudukFeatureSchema($pdo);
|
||||
$ids = $pdo->query("SELECT id FROM penduduk_miskin")->fetchAll(PDO::FETCH_COLUMN);
|
||||
foreach ($ids as $id) {
|
||||
updatePendudukPriority($pdo, (int)$id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user