Upload files to "api"

This commit is contained in:
2026-06-05 13:02:20 +00:00
parent 6f09b98d99
commit 1015a538c0
4 changed files with 281 additions and 292 deletions
+267 -286
View File
@@ -2,415 +2,351 @@
// Session configuration // Session configuration
session_start(); session_start();
// Database configuration // MySQL Database configuration
define('DB_PATH', __DIR__ . '/../db/markers.db'); define('DB_HOST', ' ');
define('DB_PORT', ' ');
define('DB_NAME', ' ');
define('DB_USER', ' ');
define('DB_PASS', ' ');
// Create db directory if it doesn't exist // Connect to MySQL database
if (!is_dir(__DIR__ . '/../db')) {
mkdir(__DIR__ . '/../db', 0755, true);
}
// Connect to SQLite database
function getDB() { function getDB() {
static $db = null;
if ($db !== null) {
return $db;
}
try { try {
$db = new PDO('sqlite:' . DB_PATH); $dsn = 'mysql:host=' . DB_HOST . ';port=' . DB_PORT . ';dbname=' . DB_NAME . ';charset=utf8mb4';
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db = new PDO($dsn, DB_USER, DB_PASS, [
$db->exec('PRAGMA foreign_keys = ON'); PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4'
]);
// Initialize database schema // Initialize database schema
initializeDB($db); initializeDB($db);
return $db; return $db;
} catch (PDOException $e) { } catch (PDOException $e) {
http_response_code(500);
die(json_encode(['error' => 'Database connection failed: ' . $e->getMessage()])); die(json_encode(['error' => 'Database connection failed: ' . $e->getMessage()]));
} }
} }
// Initialize database schema // Initialize database schema (MySQL version)
function initializeDB($db) { function initializeDB($db) {
// Create markers table for points // Create markers table for points
$db->exec(' $db->exec('
CREATE TABLE IF NOT EXISTS markers ( CREATE TABLE IF NOT EXISTS markers (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
nomor_spbu VARCHAR(100), nomor_spbu VARCHAR(100) DEFAULT NULL,
latitude REAL NOT NULL, latitude DOUBLE NOT NULL,
longitude REAL NOT NULL, longitude DOUBLE NOT NULL,
open_24_hours BOOLEAN DEFAULT 0, open_24_hours TINYINT(1) DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
'); ');
// Add nomor_spbu column to existing databases if it doesn't exist
try {
$db->exec('PRAGMA table_info(markers)');
$columns = $db->query('PRAGMA table_info(markers)')->fetchAll(PDO::FETCH_ASSOC);
$hasNomorSpbu = false;
foreach ($columns as $col) {
if ($col['name'] === 'nomor_spbu') {
$hasNomorSpbu = true;
break;
}
}
if (!$hasNomorSpbu) {
$db->exec('ALTER TABLE markers ADD COLUMN nomor_spbu VARCHAR(100)');
}
} catch (PDOException $e) {
// Table already has the column or other non-critical error
}
// Create roads table for polylines // Create roads table for polylines
$db->exec(' $db->exec('
CREATE TABLE IF NOT EXISTS roads ( CREATE TABLE IF NOT EXISTS roads (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
road_type VARCHAR(50) NOT NULL CHECK(road_type IN (\'Nasional\', \'Provinsi\', \'Kabupaten\')), road_type ENUM(\'Nasional\', \'Provinsi\', \'Kabupaten\') NOT NULL,
coordinates TEXT NOT NULL, coordinates LONGTEXT NOT NULL,
length_meters REAL NOT NULL, length_meters DOUBLE NOT NULL,
condition_status ENUM(\'normal\', \'minor_damage\', \'major_damage\') DEFAULT \'normal\',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
'); ');
// Create indexes for roads table // Create indexes for roads table
try { try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_roads_road_type ON roads(road_type)'); $db->exec('CREATE INDEX idx_roads_road_type ON roads(road_type)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_roads_created_at ON roads(created_at)'); } catch (PDOException $e) { /* Index may already exist */ }
} catch (PDOException $e) { try {
// Indexes may already exist $db->exec('CREATE INDEX idx_roads_created_at ON roads(created_at)');
} } catch (PDOException $e) { /* Index may already exist */ }
// Create land_parcels table for polygons // Create land_parcels table for polygons
$db->exec(' $db->exec('
CREATE TABLE IF NOT EXISTS land_parcels ( CREATE TABLE IF NOT EXISTS land_parcels (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
certificate_type VARCHAR(50) NOT NULL CHECK(certificate_type IN (\'SHM\', \'HGB\', \'HGU\', \'HP\')), certificate_type ENUM(\'SHM\', \'HGB\', \'HGU\', \'HP\') NOT NULL,
coordinates TEXT NOT NULL, coordinates LONGTEXT NOT NULL,
area_sqm REAL NOT NULL, area_sqm DOUBLE NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
'); ');
// Create indexes for land_parcels table // Create indexes for land_parcels table
try { try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_parcels_cert_type ON land_parcels(certificate_type)'); $db->exec('CREATE INDEX idx_parcels_cert_type ON land_parcels(certificate_type)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_parcels_created_at ON land_parcels(created_at)'); } catch (PDOException $e) { /* Index may already exist */ }
} catch (PDOException $e) {
// Indexes may already exist
}
// Add condition_status column to roads table if it doesn't exist
try { try {
$columns = $db->query('PRAGMA table_info(roads)')->fetchAll(PDO::FETCH_ASSOC); $db->exec('CREATE INDEX idx_parcels_created_at ON land_parcels(created_at)');
$hasConditionStatus = false; } catch (PDOException $e) { /* Index may already exist */ }
foreach ($columns as $col) {
if ($col['name'] === 'condition_status') {
$hasConditionStatus = true;
break;
}
}
if (!$hasConditionStatus) {
$db->exec('ALTER TABLE roads ADD COLUMN condition_status VARCHAR(20) DEFAULT \'normal\' CHECK(condition_status IN (\'normal\', \'minor_damage\', \'major_damage\'))');
}
} catch (PDOException $e) {
// Column may already exist
}
// Create pontianak_areas table for choropleth boundaries // Create pontianak_areas table for choropleth boundaries
$db->exec(' $db->exec('
CREATE TABLE IF NOT EXISTS pontianak_areas ( CREATE TABLE IF NOT EXISTS pontianak_areas (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
area_type VARCHAR(50) NOT NULL CHECK(area_type IN (\'kelurahan\', \'kecamatan\', \'city\')), area_type ENUM(\'kelurahan\', \'kecamatan\', \'city\') NOT NULL,
geometry TEXT NOT NULL, geometry LONGTEXT NOT NULL,
population INT DEFAULT 0, population INT DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE(name, area_type) UNIQUE KEY uq_area_name_type (name, area_type)
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
'); ');
// Create indexes for pontianak_areas table // Create indexes for pontianak_areas table
try { try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_pontianak_areas_type ON pontianak_areas(area_type)'); $db->exec('CREATE INDEX idx_pontianak_areas_type ON pontianak_areas(area_type)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_pontianak_areas_name ON pontianak_areas(name)'); } catch (PDOException $e) { /* Index may already exist */ }
} catch (PDOException $e) { try {
// Indexes may already exist $db->exec('CREATE INDEX idx_pontianak_areas_name ON pontianak_areas(name)');
} } catch (PDOException $e) { /* Index may already exist */ }
// Create area_statistics table for computed densities // Create area_statistics table for computed densities
$db->exec(' $db->exec('
CREATE TABLE IF NOT EXISTS area_statistics ( CREATE TABLE IF NOT EXISTS area_statistics (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INT AUTO_INCREMENT PRIMARY KEY,
pontianak_area_id INTEGER NOT NULL, pontianak_area_id INT NOT NULL,
statistic_type VARCHAR(50) NOT NULL CHECK(statistic_type IN (\'population_density\', \'road_density\', \'damaged_roads_density\')), statistic_type ENUM(\'population_density\', \'road_density\', \'damaged_roads_density\') NOT NULL,
value REAL NOT NULL, value DOUBLE NOT NULL,
data_date DATE NOT NULL, data_date DATE NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY(pontianak_area_id) REFERENCES pontianak_areas(id) ON DELETE CASCADE, UNIQUE KEY uq_area_stat_date (pontianak_area_id, statistic_type, data_date),
UNIQUE(pontianak_area_id, statistic_type, data_date) FOREIGN KEY (pontianak_area_id) REFERENCES pontianak_areas(id) ON DELETE CASCADE
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
'); ');
// Create indexes for area_statistics table // Create indexes for area_statistics table
try { try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_stats_area_type ON area_statistics(pontianak_area_id, statistic_type)'); $db->exec('CREATE INDEX idx_stats_area_type ON area_statistics(pontianak_area_id, statistic_type)');
} catch (PDOException $e) { } catch (PDOException $e) { /* Index may already exist */ }
// Indexes may already exist
}
// Create masjids table // Create masjids table
$db->exec(' $db->exec('
CREATE TABLE IF NOT EXISTS masjids ( CREATE TABLE IF NOT EXISTS masjids (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
latitude REAL NOT NULL, latitude DOUBLE NOT NULL,
longitude REAL NOT NULL, longitude DOUBLE NOT NULL,
radius_meters REAL NOT NULL DEFAULT 500, radius_meters DOUBLE NOT NULL DEFAULT 500,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
'); ');
// Create indexes for masjids table // Create indexes for masjids table
try { try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_masjids_created_at ON masjids(created_at)'); $db->exec('CREATE INDEX idx_masjids_created_at ON masjids(created_at)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_masjids_name ON masjids(name)'); } catch (PDOException $e) { /* Index may already exist */ }
} catch (PDOException $e) { try {
// Indexes may already exist $db->exec('CREATE INDEX idx_masjids_name ON masjids(name)');
} } catch (PDOException $e) { /* Index may already exist */ }
// Create special needs points table // Create special needs points table
$db->exec(' $db->exec('
CREATE TABLE IF NOT EXISTS need_points ( CREATE TABLE IF NOT EXISTS need_points (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
latitude REAL NOT NULL, latitude DOUBLE NOT NULL,
longitude REAL NOT NULL, longitude DOUBLE NOT NULL,
masjid_id INTEGER NOT NULL, masjid_id INT NOT NULL,
household_name VARCHAR(255), household_name VARCHAR(255) DEFAULT NULL,
head_name VARCHAR(255), head_name VARCHAR(255) DEFAULT NULL,
economic_status VARCHAR(50), economic_status VARCHAR(50) DEFAULT NULL,
is_verified INTEGER NOT NULL DEFAULT 0, is_verified TINYINT(1) NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY(masjid_id) REFERENCES masjids(id) ON DELETE CASCADE FOREIGN KEY (masjid_id) REFERENCES masjids(id) ON DELETE CASCADE
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
'); ');
// Create indexes for need_points table // Create indexes for need_points table
try { try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_need_points_masjid_id ON need_points(masjid_id)'); $db->exec('CREATE INDEX idx_need_points_masjid_id ON need_points(masjid_id)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_need_points_created_at ON need_points(created_at)'); } catch (PDOException $e) { /* Index may already exist */ }
} catch (PDOException $e) {
// Indexes may already exist
}
// Add new columns to existing need_points table if they don't exist
try { try {
$columns = $db->query('PRAGMA table_info(need_points)')->fetchAll(PDO::FETCH_ASSOC); $db->exec('CREATE INDEX idx_need_points_created_at ON need_points(created_at)');
$hasHouseholdName = false; } catch (PDOException $e) { /* Index may already exist */ }
$hasHeadName = false;
$hasEconomic = false;
$hasVerified = false;
foreach ($columns as $col) {
if ($col['name'] === 'household_name') $hasHouseholdName = true;
if ($col['name'] === 'head_name') $hasHeadName = true;
if ($col['name'] === 'economic_status') $hasEconomic = true;
if ($col['name'] === 'is_verified') $hasVerified = true;
}
if (!$hasHouseholdName) {
$db->exec('ALTER TABLE need_points ADD COLUMN household_name VARCHAR(255)');
}
if (!$hasHeadName) {
$db->exec('ALTER TABLE need_points ADD COLUMN head_name VARCHAR(255)');
}
if (!$hasEconomic) {
$db->exec('ALTER TABLE need_points ADD COLUMN economic_status VARCHAR(50)');
}
if (!$hasVerified) {
$db->exec('ALTER TABLE need_points ADD COLUMN is_verified INTEGER NOT NULL DEFAULT 0');
}
} catch (PDOException $e) {
// non-critical
}
// Create households table for poverty alleviation base data // Create households table for poverty alleviation base data
$db->exec(' $db->exec("
CREATE TABLE IF NOT EXISTS households ( CREATE TABLE IF NOT EXISTS households (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INT AUTO_INCREMENT PRIMARY KEY,
household_code VARCHAR(100) NOT NULL UNIQUE, household_code VARCHAR(100) NOT NULL UNIQUE,
head_name VARCHAR(255) NOT NULL, head_name VARCHAR(255) NOT NULL,
nik_hash VARCHAR(255), nik_hash VARCHAR(255) DEFAULT NULL,
latitude REAL NOT NULL, latitude DOUBLE NOT NULL,
longitude REAL NOT NULL, longitude DOUBLE NOT NULL,
need_point_id INTEGER UNIQUE, need_point_id INT DEFAULT NULL UNIQUE,
masjid_id INTEGER, masjid_id INT DEFAULT NULL,
pontianak_area_id INTEGER, pontianak_area_id INT DEFAULT NULL,
monthly_income REAL NOT NULL DEFAULT 0, monthly_income DOUBLE NOT NULL DEFAULT 0,
dependents_count INTEGER NOT NULL DEFAULT 0, dependents_count INT NOT NULL DEFAULT 0,
housing_condition_score INTEGER NOT NULL DEFAULT 3 CHECK(housing_condition_score BETWEEN 1 AND 5), housing_condition_score TINYINT NOT NULL DEFAULT 3,
employment_status VARCHAR(50) NOT NULL DEFAULT \'unknown\' CHECK(employment_status IN (\'formal\', \'informal\', \'unemployed\', \'daily_worker\', \'micro_business\', \'retired\', \'unknown\')), employment_status ENUM('formal', 'informal', 'unemployed', 'daily_worker', 'micro_business', 'retired', 'unknown') NOT NULL DEFAULT 'unknown',
economic_status VARCHAR(50) NOT NULL DEFAULT \'unknown\' CHECK(economic_status IN (\'sangat_miskin\', \'miskin\', \'rentan\', \'cukup\', \'baik\', \'unknown\')), economic_status ENUM('sangat_miskin', 'miskin', 'rentan', 'cukup', 'baik', 'unknown') NOT NULL DEFAULT 'unknown',
verification_status VARCHAR(30) NOT NULL DEFAULT \'unverified\' CHECK(verification_status IN (\'unverified\', \'field_verified\', \'admin_approved\', \'rejected\', \'needs_review\')), verification_status ENUM('unverified', 'field_verified', 'admin_approved', 'rejected', 'needs_review') NOT NULL DEFAULT 'unverified',
vulnerability_notes TEXT, vulnerability_notes TEXT DEFAULT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY(need_point_id) REFERENCES need_points(id) ON DELETE SET NULL, FOREIGN KEY (need_point_id) REFERENCES need_points(id) ON DELETE SET NULL,
FOREIGN KEY(masjid_id) REFERENCES masjids(id) ON DELETE SET NULL, FOREIGN KEY (masjid_id) REFERENCES masjids(id) ON DELETE SET NULL,
FOREIGN KEY(pontianak_area_id) REFERENCES pontianak_areas(id) ON DELETE SET NULL FOREIGN KEY (pontianak_area_id) REFERENCES pontianak_areas(id) ON DELETE SET NULL
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
'); ");
// Create indexes for households table // Create indexes for households table
try { try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_households_code ON households(household_code)'); $db->exec('CREATE INDEX idx_households_code ON households(household_code)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_households_verification_status ON households(verification_status)'); } catch (PDOException $e) { /* Index may already exist */ }
$db->exec('CREATE INDEX IF NOT EXISTS idx_households_masjid_id ON households(masjid_id)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_households_area_id ON households(pontianak_area_id)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_households_created_at ON households(created_at)');
} catch (PDOException $e) {
// Indexes may already exist
}
// Add economic_status column to existing households table if it doesn't exist
try { try {
$columns = $db->query('PRAGMA table_info(households)')->fetchAll(PDO::FETCH_ASSOC); $db->exec('CREATE INDEX idx_households_verification_status ON households(verification_status)');
$hasEconomicStatus = false; } catch (PDOException $e) { /* Index may already exist */ }
foreach ($columns as $col) { try {
if ($col['name'] === 'economic_status') { $db->exec('CREATE INDEX idx_households_masjid_id ON households(masjid_id)');
$hasEconomicStatus = true; } catch (PDOException $e) { /* Index may already exist */ }
break; try {
} $db->exec('CREATE INDEX idx_households_area_id ON households(pontianak_area_id)');
} } catch (PDOException $e) { /* Index may already exist */ }
if (!$hasEconomicStatus) { try {
$db->exec("ALTER TABLE households ADD COLUMN economic_status VARCHAR(50) NOT NULL DEFAULT 'unknown'"); $db->exec('CREATE INDEX idx_households_created_at ON households(created_at)');
} } catch (PDOException $e) { /* Index may already exist */ }
} catch (PDOException $e) {
// non-critical
}
// Create assistance distributions table // Create assistance distributions table
$db->exec(' $db->exec("
CREATE TABLE IF NOT EXISTS assistance_distributions ( CREATE TABLE IF NOT EXISTS assistance_distributions (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INT AUTO_INCREMENT PRIMARY KEY,
household_id INTEGER NOT NULL, household_id INT NOT NULL,
assistance_type VARCHAR(50) NOT NULL CHECK(assistance_type IN (\'pangan\', \'tunai\', \'kesehatan\', \'pendidikan\', \'usaha_mikro\', \'lainnya\')), assistance_type ENUM('pangan', 'tunai', 'kesehatan', 'pendidikan', 'usaha_mikro', 'lainnya') NOT NULL,
assistance_value REAL NOT NULL DEFAULT 0, assistance_value DOUBLE NOT NULL DEFAULT 0,
assistance_frequency VARCHAR(30) NOT NULL DEFAULT \'sekali\' CHECK(assistance_frequency IN (\'sekali\', \'mingguan\', \'bulanan\', \'triwulanan\', \'tahunan\', \'insidental\')), assistance_frequency ENUM('sekali', 'mingguan', 'bulanan', 'triwulanan', 'tahunan', 'insidental') NOT NULL DEFAULT 'sekali',
distribution_date DATE NOT NULL, distribution_date DATE NOT NULL,
delivery_status VARCHAR(30) NOT NULL DEFAULT \'pending\' CHECK(delivery_status IN (\'pending\', \'delivered\', \'failed\')), delivery_status ENUM('pending', 'delivered', 'failed') NOT NULL DEFAULT 'pending',
delivered_by VARCHAR(255), delivered_by VARCHAR(255) DEFAULT NULL,
notes TEXT, notes TEXT DEFAULT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY(household_id) REFERENCES households(id) ON DELETE CASCADE FOREIGN KEY (household_id) REFERENCES households(id) ON DELETE CASCADE
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
'); ");
// Create indexes for assistance distributions table // Create indexes for assistance distributions table
try { try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_assistance_household_id ON assistance_distributions(household_id)'); $db->exec('CREATE INDEX idx_assistance_household_id ON assistance_distributions(household_id)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_assistance_date ON assistance_distributions(distribution_date)'); } catch (PDOException $e) { /* Index may already exist */ }
$db->exec('CREATE INDEX IF NOT EXISTS idx_assistance_status ON assistance_distributions(delivery_status)'); try {
} catch (PDOException $e) { $db->exec('CREATE INDEX idx_assistance_date ON assistance_distributions(distribution_date)');
// Indexes may already exist } catch (PDOException $e) { /* Index may already exist */ }
} try {
$db->exec('CREATE INDEX idx_assistance_status ON assistance_distributions(delivery_status)');
} catch (PDOException $e) { /* Index may already exist */ }
// Create verification logs table // Create verification logs table
$db->exec(' $db->exec("
CREATE TABLE IF NOT EXISTS verification_logs ( CREATE TABLE IF NOT EXISTS verification_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INT AUTO_INCREMENT PRIMARY KEY,
household_id INTEGER NOT NULL, household_id INT NOT NULL,
verification_status VARCHAR(30) NOT NULL CHECK(verification_status IN (\'field_verified\', \'admin_approved\', \'rejected\', \'needs_review\')), verification_status ENUM('field_verified', 'admin_approved', 'rejected', 'needs_review') NOT NULL,
verifier_name VARCHAR(255) NOT NULL, verifier_name VARCHAR(255) NOT NULL,
verifier_role VARCHAR(100), verifier_role VARCHAR(100) DEFAULT NULL,
field_note TEXT, field_note TEXT DEFAULT NULL,
evidence_photo_count INTEGER NOT NULL DEFAULT 0, evidence_photo_count INT NOT NULL DEFAULT 0,
data_confidence_score REAL, data_confidence_score DOUBLE DEFAULT NULL,
verified_at DATETIME DEFAULT CURRENT_TIMESTAMP, verified_at DATETIME DEFAULT CURRENT_TIMESTAMP,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(household_id) REFERENCES households(id) ON DELETE CASCADE FOREIGN KEY (household_id) REFERENCES households(id) ON DELETE CASCADE
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
'); ");
// Create indexes for verification logs table // Create indexes for verification logs table
try { try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_verification_household_id ON verification_logs(household_id)'); $db->exec('CREATE INDEX idx_verification_household_id ON verification_logs(household_id)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_verification_status ON verification_logs(verification_status)'); } catch (PDOException $e) { /* Index may already exist */ }
$db->exec('CREATE INDEX IF NOT EXISTS idx_verification_verified_at ON verification_logs(verified_at)'); try {
} catch (PDOException $e) { $db->exec('CREATE INDEX idx_verification_status ON verification_logs(verification_status)');
// Indexes may already exist } catch (PDOException $e) { /* Index may already exist */ }
} try {
$db->exec('CREATE INDEX idx_verification_verified_at ON verification_logs(verified_at)');
} catch (PDOException $e) { /* Index may already exist */ }
// Create household scores table (Option B) // Create household scores table
$db->exec(' $db->exec("
CREATE TABLE IF NOT EXISTS household_scores ( CREATE TABLE IF NOT EXISTS household_scores (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INT AUTO_INCREMENT PRIMARY KEY,
household_id INTEGER NOT NULL UNIQUE, household_id INT NOT NULL UNIQUE,
skr REAL NOT NULL DEFAULT 0, skr DOUBLE NOT NULL DEFAULT 0,
sag REAL NOT NULL DEFAULT 0, sag DOUBLE NOT NULL DEFAULT 0,
spi REAL NOT NULL DEFAULT 0, spi DOUBLE NOT NULL DEFAULT 0,
priority_level VARCHAR(30) NOT NULL DEFAULT \'low\' CHECK(priority_level IN (\'very_high\', \'high\', \'medium\', \'low\')), priority_level ENUM('very_high', 'high', 'medium', 'low') NOT NULL DEFAULT 'low',
score_version VARCHAR(30) NOT NULL DEFAULT \'v1\', score_version VARCHAR(30) NOT NULL DEFAULT 'v1',
components_json TEXT, components_json TEXT DEFAULT NULL,
computed_at DATETIME DEFAULT CURRENT_TIMESTAMP, computed_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY(household_id) REFERENCES households(id) ON DELETE CASCADE FOREIGN KEY (household_id) REFERENCES households(id) ON DELETE CASCADE
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
'); ");
// Create indexes for household_scores table // Create indexes for household_scores table
try { try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_household_scores_spi ON household_scores(spi DESC)'); $db->exec('CREATE INDEX idx_household_scores_spi ON household_scores(spi)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_household_scores_priority ON household_scores(priority_level)'); } catch (PDOException $e) { /* Index may already exist */ }
$db->exec('CREATE INDEX IF NOT EXISTS idx_household_scores_computed_at ON household_scores(computed_at)'); try {
} catch (PDOException $e) { $db->exec('CREATE INDEX idx_household_scores_priority ON household_scores(priority_level)');
// Indexes may already exist } catch (PDOException $e) { /* Index may already exist */ }
} try {
$db->exec('CREATE INDEX idx_household_scores_computed_at ON household_scores(computed_at)');
} catch (PDOException $e) { /* Index may already exist */ }
// Create score metadata table for versioning/tuning // Create score metadata table for versioning/tuning
$db->exec(' $db->exec('
CREATE TABLE IF NOT EXISTS score_metadata ( CREATE TABLE IF NOT EXISTS score_metadata (
key_name VARCHAR(100) PRIMARY KEY, key_name VARCHAR(100) PRIMARY KEY,
key_value TEXT NOT NULL, key_value TEXT NOT NULL,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
'); ');
// Create users table for authentication // Create users table for authentication
$db->exec(' $db->exec("
CREATE TABLE IF NOT EXISTS users ( CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) NOT NULL UNIQUE, username VARCHAR(100) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE, email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL, password_hash VARCHAR(255) NOT NULL,
role VARCHAR(50) NOT NULL DEFAULT \'Petugas\' CHECK(role IN (\'Admin\', \'Petugas\', \'Pimpinan\')), role ENUM('Admin', 'Petugas', 'Pimpinan') NOT NULL DEFAULT 'Petugas',
is_active BOOLEAN NOT NULL DEFAULT 1, is_active TINYINT(1) NOT NULL DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
'); ");
// Create indexes for users table // Create indexes for users table
try { try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)'); $db->exec('CREATE INDEX idx_users_email ON users(email)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_users_username ON users(username)'); } catch (PDOException $e) { /* Index may already exist */ }
$db->exec('CREATE INDEX IF NOT EXISTS idx_users_role ON users(role)'); try {
} catch (PDOException $e) { $db->exec('CREATE INDEX idx_users_username ON users(username)');
// Indexes may already exist } catch (PDOException $e) { /* Index may already exist */ }
} try {
$db->exec('CREATE INDEX idx_users_role ON users(role)');
} catch (PDOException $e) { /* Index may already exist */ }
// Initialize default admin user // Initialize default admin user
try { try {
$adminCount = $db->query('SELECT COUNT(*) as cnt FROM users WHERE role = \'Admin\'')->fetch(PDO::FETCH_ASSOC); $adminCount = $db->query("SELECT COUNT(*) as cnt FROM users WHERE role = 'Admin'")->fetch(PDO::FETCH_ASSOC);
if ($adminCount['cnt'] == 0) { if ($adminCount['cnt'] == 0) {
$adminPassword = password_hash('admin123', PASSWORD_BCRYPT, ['cost' => 12]); $adminPassword = password_hash('admin123', PASSWORD_BCRYPT, ['cost' => 12]);
$stmt = $db->prepare('INSERT INTO users (username, email, password_hash, role, is_active) VALUES (?, ?, ?, ?, 1)'); $stmt = $db->prepare('INSERT INTO users (username, email, password_hash, role, is_active) VALUES (?, ?, ?, ?, 1)');
@@ -433,7 +369,7 @@ function initializeDB($db) {
'population_reference' => '288315899' 'population_reference' => '288315899'
]; ];
$metaStmt = $db->prepare('INSERT OR IGNORE INTO score_metadata (key_name, key_value) VALUES (?, ?)'); $metaStmt = $db->prepare('INSERT IGNORE INTO score_metadata (key_name, key_value) VALUES (?, ?)');
foreach ($defaultMeta as $key => $value) { foreach ($defaultMeta as $key => $value) {
$metaStmt->execute([$key, $value]); $metaStmt->execute([$key, $value]);
} }
@@ -895,15 +831,55 @@ function syncNeedPointsToHouseholds($db) {
$updatedCount = 0; $updatedCount = 0;
$insertedCount = 0; $insertedCount = 0;
$updateStmt = $db->prepare("\n UPDATE households\n SET economic_status = (\n SELECT COALESCE(NULLIF(TRIM(np.economic_status), ''), 'unknown')\n FROM need_points np\n WHERE np.id = households.need_point_id\n ),\n updated_at = CURRENT_TIMESTAMP\n WHERE need_point_id IS NOT NULL\n AND (economic_status IS NULL OR TRIM(economic_status) = '')\n "); $updateStmt = $db->prepare("
UPDATE households
SET economic_status = (
SELECT COALESCE(NULLIF(TRIM(np.economic_status), ''), 'unknown')
FROM need_points np
WHERE np.id = households.need_point_id
),
updated_at = CURRENT_TIMESTAMP
WHERE need_point_id IS NOT NULL
AND (economic_status IS NULL OR TRIM(economic_status) = '')
");
$updateStmt->execute(); $updateStmt->execute();
$updatedCount += $updateStmt->rowCount(); $updatedCount += $updateStmt->rowCount();
$missingStmt = $db->query("\n SELECT\n np.id,\n np.name,\n np.latitude,\n np.longitude,\n np.masjid_id,\n np.head_name,\n np.economic_status,\n np.is_verified\n FROM need_points np\n LEFT JOIN households h ON h.need_point_id = np.id\n WHERE h.id IS NULL\n ORDER BY np.id ASC\n "); $missingStmt = $db->query("
SELECT
np.id,
np.name,
np.latitude,
np.longitude,
np.masjid_id,
np.head_name,
np.economic_status,
np.is_verified
FROM need_points np
LEFT JOIN households h ON h.need_point_id = np.id
WHERE h.id IS NULL
ORDER BY np.id ASC
");
$missingRows = $missingStmt->fetchAll(PDO::FETCH_ASSOC); $missingRows = $missingStmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($missingRows)) { if (!empty($missingRows)) {
$insertStmt = $db->prepare("\n INSERT INTO households (\n household_code,\n head_name,\n latitude,\n longitude,\n need_point_id,\n masjid_id,\n monthly_income,\n dependents_count,\n housing_condition_score,\n employment_status,\n economic_status,\n verification_status,\n vulnerability_notes\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n "); $insertStmt = $db->prepare("
INSERT INTO households (
household_code,
head_name,
latitude,
longitude,
need_point_id,
masjid_id,
monthly_income,
dependents_count,
housing_condition_score,
employment_status,
economic_status,
verification_status,
vulnerability_notes
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
");
foreach ($missingRows as $row) { foreach ($missingRows as $row) {
$pointId = (int)$row['id']; $pointId = (int)$row['id'];
@@ -945,7 +921,13 @@ function syncNeedPointsToHouseholds($db) {
} }
function ensureHouseholdScoresForAllHouseholds($db) { function ensureHouseholdScoresForAllHouseholds($db) {
$missingStmt = $db->query("\n SELECT h.*\n FROM households h\n LEFT JOIN household_scores hs ON hs.household_id = h.id\n WHERE hs.household_id IS NULL\n ORDER BY h.id ASC\n "); $missingStmt = $db->query("
SELECT h.*
FROM households h
LEFT JOIN household_scores hs ON hs.household_id = h.id
WHERE hs.household_id IS NULL
ORDER BY h.id ASC
");
$missingHouseholds = $missingStmt->fetchAll(PDO::FETCH_ASSOC); $missingHouseholds = $missingStmt->fetchAll(PDO::FETCH_ASSOC);
$computed = 0; $computed = 0;
@@ -1160,14 +1142,13 @@ function upsertHouseholdScore($db, $householdId, $scoreData) {
updated_at updated_at
) )
VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT(household_id) ON DUPLICATE KEY UPDATE
DO UPDATE SET skr = VALUES(skr),
skr = excluded.skr, sag = VALUES(sag),
sag = excluded.sag, spi = VALUES(spi),
spi = excluded.spi, priority_level = VALUES(priority_level),
priority_level = excluded.priority_level, score_version = VALUES(score_version),
score_version = excluded.score_version, components_json = VALUES(components_json),
components_json = excluded.components_json,
computed_at = CURRENT_TIMESTAMP, computed_at = CURRENT_TIMESTAMP,
updated_at = CURRENT_TIMESTAMP updated_at = CURRENT_TIMESTAMP
'); ');
+11 -3
View File
@@ -13,11 +13,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
} }
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = json_decode(file_get_contents('php://input'), true); $rawData = file_get_contents('php://input');
$data = json_decode($rawData, true);
if (!isset($data['email']) || !isset($data['password'])) { if (!$data) {
$data = $_POST;
}
if (empty($data['email']) || empty($data['password'])) {
http_response_code(400); http_response_code(400);
die(json_encode(['error' => 'Email and password are required'])); die(json_encode([
'error' => 'Email and password are required',
'debug' => ['raw' => $rawData, 'post' => $_POST]
]));
} }
$result = loginUser($data['email'], $data['password']); $result = loginUser($data['email'], $data['password']);
+1 -1
View File
@@ -35,7 +35,7 @@ try {
} }
$db = getDB(); $db = getDB();
$stmt = $db->prepare('INSERT INTO score_metadata (key_name, key_value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(key_name) DO UPDATE SET key_value = excluded.key_value, updated_at = CURRENT_TIMESTAMP'); $stmt = $db->prepare('INSERT INTO score_metadata (key_name, key_value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON DUPLICATE KEY UPDATE key_value = VALUES(key_value), updated_at = CURRENT_TIMESTAMP');
$stmt->execute(['target_nasional', number_format($targetNasional, 2, '.', '')]); $stmt->execute(['target_nasional', number_format($targetNasional, 2, '.', '')]);
$stmt->execute(['population_reference', (string)$populationReference]); $stmt->execute(['population_reference', (string)$populationReference]);
+2 -2
View File
@@ -77,13 +77,13 @@ try {
// Insert Pontianak areas // Insert Pontianak areas
$stmt = $db->prepare(' $stmt = $db->prepare('
INSERT OR IGNORE INTO pontianak_areas (name, area_type, geometry, population) INSERT IGNORE INTO pontianak_areas (name, area_type, geometry, population)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?, ?)
'); ');
$today = date('Y-m-d'); $today = date('Y-m-d');
$stats_stmt = $db->prepare(' $stats_stmt = $db->prepare('
INSERT OR IGNORE INTO area_statistics (pontianak_area_id, statistic_type, value, data_date) INSERT IGNORE INTO area_statistics (pontianak_area_id, statistic_type, value, data_date)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?, ?)
'); ');