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_start();
// Database configuration
define('DB_PATH', __DIR__ . '/../db/markers.db');
// MySQL Database configuration
define('DB_HOST', ' ');
define('DB_PORT', ' ');
define('DB_NAME', ' ');
define('DB_USER', ' ');
define('DB_PASS', ' ');
// Create db directory if it doesn't exist
if (!is_dir(__DIR__ . '/../db')) {
mkdir(__DIR__ . '/../db', 0755, true);
}
// Connect to SQLite database
// Connect to MySQL database
function getDB() {
static $db = null;
if ($db !== null) {
return $db;
}
try {
$db = new PDO('sqlite:' . DB_PATH);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->exec('PRAGMA foreign_keys = ON');
$dsn = 'mysql:host=' . DB_HOST . ';port=' . DB_PORT . ';dbname=' . DB_NAME . ';charset=utf8mb4';
$db = new PDO($dsn, DB_USER, DB_PASS, [
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
initializeDB($db);
return $db;
} catch (PDOException $e) {
http_response_code(500);
die(json_encode(['error' => 'Database connection failed: ' . $e->getMessage()]));
}
}
// Initialize database schema
// Initialize database schema (MySQL version)
function initializeDB($db) {
// Create markers table for points
$db->exec('
CREATE TABLE IF NOT EXISTS markers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
nomor_spbu VARCHAR(100),
latitude REAL NOT NULL,
longitude REAL NOT NULL,
open_24_hours BOOLEAN DEFAULT 0,
nomor_spbu VARCHAR(100) DEFAULT NULL,
latitude DOUBLE NOT NULL,
longitude DOUBLE NOT NULL,
open_24_hours TINYINT(1) DEFAULT 0,
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
$db->exec('
CREATE TABLE IF NOT EXISTS roads (
id INTEGER PRIMARY KEY AUTOINCREMENT,
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
road_type VARCHAR(50) NOT NULL CHECK(road_type IN (\'Nasional\', \'Provinsi\', \'Kabupaten\')),
coordinates TEXT NOT NULL,
length_meters REAL NOT NULL,
road_type ENUM(\'Nasional\', \'Provinsi\', \'Kabupaten\') NOT NULL,
coordinates LONGTEXT NOT NULL,
length_meters DOUBLE NOT NULL,
condition_status ENUM(\'normal\', \'minor_damage\', \'major_damage\') DEFAULT \'normal\',
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
try {
$db->exec('CREATE INDEX IF NOT EXISTS 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) {
// Indexes may already exist
}
$db->exec('CREATE INDEX idx_roads_road_type ON roads(road_type)');
} catch (PDOException $e) { /* Index may already exist */ }
try {
$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
$db->exec('
CREATE TABLE IF NOT EXISTS land_parcels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
certificate_type VARCHAR(50) NOT NULL CHECK(certificate_type IN (\'SHM\', \'HGB\', \'HGU\', \'HP\')),
coordinates TEXT NOT NULL,
area_sqm REAL NOT NULL,
certificate_type ENUM(\'SHM\', \'HGB\', \'HGU\', \'HP\') NOT NULL,
coordinates LONGTEXT NOT NULL,
area_sqm DOUBLE NOT NULL,
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
try {
$db->exec('CREATE INDEX IF NOT EXISTS 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) {
// Indexes may already exist
}
// Add condition_status column to roads table if it doesn't exist
$db->exec('CREATE INDEX idx_parcels_cert_type ON land_parcels(certificate_type)');
} catch (PDOException $e) { /* Index may already exist */ }
try {
$columns = $db->query('PRAGMA table_info(roads)')->fetchAll(PDO::FETCH_ASSOC);
$hasConditionStatus = false;
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
}
$db->exec('CREATE INDEX idx_parcels_created_at ON land_parcels(created_at)');
} catch (PDOException $e) { /* Index may already exist */ }
// Create pontianak_areas table for choropleth boundaries
$db->exec('
CREATE TABLE IF NOT EXISTS pontianak_areas (
id INTEGER PRIMARY KEY AUTOINCREMENT,
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
area_type VARCHAR(50) NOT NULL CHECK(area_type IN (\'kelurahan\', \'kecamatan\', \'city\')),
geometry TEXT NOT NULL,
area_type ENUM(\'kelurahan\', \'kecamatan\', \'city\') NOT NULL,
geometry LONGTEXT NOT NULL,
population INT DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE(name, area_type)
)
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uq_area_name_type (name, area_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
');
// Create indexes for pontianak_areas table
try {
$db->exec('CREATE INDEX IF NOT EXISTS 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) {
// Indexes may already exist
}
$db->exec('CREATE INDEX idx_pontianak_areas_type ON pontianak_areas(area_type)');
} catch (PDOException $e) { /* Index may already exist */ }
try {
$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
$db->exec('
CREATE TABLE IF NOT EXISTS area_statistics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
pontianak_area_id INTEGER NOT NULL,
statistic_type VARCHAR(50) NOT NULL CHECK(statistic_type IN (\'population_density\', \'road_density\', \'damaged_roads_density\')),
value REAL NOT NULL,
id INT AUTO_INCREMENT PRIMARY KEY,
pontianak_area_id INT NOT NULL,
statistic_type ENUM(\'population_density\', \'road_density\', \'damaged_roads_density\') NOT NULL,
value DOUBLE NOT NULL,
data_date DATE NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(pontianak_area_id) REFERENCES pontianak_areas(id) ON DELETE CASCADE,
UNIQUE(pontianak_area_id, statistic_type, data_date)
)
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uq_area_stat_date (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
try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_stats_area_type ON area_statistics(pontianak_area_id, statistic_type)');
} catch (PDOException $e) {
// Indexes may already exist
}
$db->exec('CREATE INDEX idx_stats_area_type ON area_statistics(pontianak_area_id, statistic_type)');
} catch (PDOException $e) { /* Index may already exist */ }
// Create masjids table
$db->exec('
CREATE TABLE IF NOT EXISTS masjids (
id INTEGER PRIMARY KEY AUTOINCREMENT,
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
latitude REAL NOT NULL,
longitude REAL NOT NULL,
radius_meters REAL NOT NULL DEFAULT 500,
latitude DOUBLE NOT NULL,
longitude DOUBLE NOT NULL,
radius_meters DOUBLE NOT NULL DEFAULT 500,
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
try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_masjids_created_at ON masjids(created_at)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_masjids_name ON masjids(name)');
} catch (PDOException $e) {
// Indexes may already exist
}
$db->exec('CREATE INDEX idx_masjids_created_at ON masjids(created_at)');
} catch (PDOException $e) { /* Index may already exist */ }
try {
$db->exec('CREATE INDEX idx_masjids_name ON masjids(name)');
} catch (PDOException $e) { /* Index may already exist */ }
// Create special needs points table
$db->exec('
CREATE TABLE IF NOT EXISTS need_points (
id INTEGER PRIMARY KEY AUTOINCREMENT,
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
latitude REAL NOT NULL,
longitude REAL NOT NULL,
masjid_id INTEGER NOT NULL,
household_name VARCHAR(255),
head_name VARCHAR(255),
economic_status VARCHAR(50),
is_verified INTEGER NOT NULL DEFAULT 0,
latitude DOUBLE NOT NULL,
longitude DOUBLE NOT NULL,
masjid_id INT NOT NULL,
household_name VARCHAR(255) DEFAULT NULL,
head_name VARCHAR(255) DEFAULT NULL,
economic_status VARCHAR(50) DEFAULT NULL,
is_verified TINYINT(1) NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(masjid_id) REFERENCES masjids(id) ON DELETE CASCADE
)
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (masjid_id) REFERENCES masjids(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
');
// Create indexes for need_points table
try {
$db->exec('CREATE INDEX IF NOT EXISTS 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) {
// Indexes may already exist
}
// Add new columns to existing need_points table if they don't exist
$db->exec('CREATE INDEX idx_need_points_masjid_id ON need_points(masjid_id)');
} catch (PDOException $e) { /* Index may already exist */ }
try {
$columns = $db->query('PRAGMA table_info(need_points)')->fetchAll(PDO::FETCH_ASSOC);
$hasHouseholdName = false;
$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
}
$db->exec('CREATE INDEX idx_need_points_created_at ON need_points(created_at)');
} catch (PDOException $e) { /* Index may already exist */ }
// Create households table for poverty alleviation base data
$db->exec('
$db->exec("
CREATE TABLE IF NOT EXISTS households (
id INTEGER PRIMARY KEY AUTOINCREMENT,
id INT AUTO_INCREMENT PRIMARY KEY,
household_code VARCHAR(100) NOT NULL UNIQUE,
head_name VARCHAR(255) NOT NULL,
nik_hash VARCHAR(255),
latitude REAL NOT NULL,
longitude REAL NOT NULL,
need_point_id INTEGER UNIQUE,
masjid_id INTEGER,
pontianak_area_id INTEGER,
monthly_income REAL NOT NULL DEFAULT 0,
dependents_count INTEGER NOT NULL DEFAULT 0,
housing_condition_score INTEGER NOT NULL DEFAULT 3 CHECK(housing_condition_score BETWEEN 1 AND 5),
employment_status VARCHAR(50) NOT NULL DEFAULT \'unknown\' CHECK(employment_status IN (\'formal\', \'informal\', \'unemployed\', \'daily_worker\', \'micro_business\', \'retired\', \'unknown\')),
economic_status VARCHAR(50) NOT NULL DEFAULT \'unknown\' CHECK(economic_status IN (\'sangat_miskin\', \'miskin\', \'rentan\', \'cukup\', \'baik\', \'unknown\')),
verification_status VARCHAR(30) NOT NULL DEFAULT \'unverified\' CHECK(verification_status IN (\'unverified\', \'field_verified\', \'admin_approved\', \'rejected\', \'needs_review\')),
vulnerability_notes TEXT,
nik_hash VARCHAR(255) DEFAULT NULL,
latitude DOUBLE NOT NULL,
longitude DOUBLE NOT NULL,
need_point_id INT DEFAULT NULL UNIQUE,
masjid_id INT DEFAULT NULL,
pontianak_area_id INT DEFAULT NULL,
monthly_income DOUBLE NOT NULL DEFAULT 0,
dependents_count INT NOT NULL DEFAULT 0,
housing_condition_score TINYINT NOT NULL DEFAULT 3,
employment_status ENUM('formal', 'informal', 'unemployed', 'daily_worker', 'micro_business', 'retired', 'unknown') NOT NULL DEFAULT 'unknown',
economic_status ENUM('sangat_miskin', 'miskin', 'rentan', 'cukup', 'baik', 'unknown') NOT NULL DEFAULT 'unknown',
verification_status ENUM('unverified', 'field_verified', 'admin_approved', 'rejected', 'needs_review') NOT NULL DEFAULT 'unverified',
vulnerability_notes TEXT DEFAULT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
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(pontianak_area_id) REFERENCES pontianak_areas(id) ON DELETE SET NULL
)
');
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 (masjid_id) REFERENCES masjids(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
try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_households_code ON households(household_code)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_households_verification_status ON households(verification_status)');
$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
$db->exec('CREATE INDEX idx_households_code ON households(household_code)');
} catch (PDOException $e) { /* Index may already exist */ }
try {
$columns = $db->query('PRAGMA table_info(households)')->fetchAll(PDO::FETCH_ASSOC);
$hasEconomicStatus = false;
foreach ($columns as $col) {
if ($col['name'] === 'economic_status') {
$hasEconomicStatus = true;
break;
}
}
if (!$hasEconomicStatus) {
$db->exec("ALTER TABLE households ADD COLUMN economic_status VARCHAR(50) NOT NULL DEFAULT 'unknown'");
}
} catch (PDOException $e) {
// non-critical
}
$db->exec('CREATE INDEX idx_households_verification_status ON households(verification_status)');
} catch (PDOException $e) { /* Index may already exist */ }
try {
$db->exec('CREATE INDEX idx_households_masjid_id ON households(masjid_id)');
} catch (PDOException $e) { /* Index may already exist */ }
try {
$db->exec('CREATE INDEX idx_households_area_id ON households(pontianak_area_id)');
} catch (PDOException $e) { /* Index may already exist */ }
try {
$db->exec('CREATE INDEX idx_households_created_at ON households(created_at)');
} catch (PDOException $e) { /* Index may already exist */ }
// Create assistance distributions table
$db->exec('
$db->exec("
CREATE TABLE IF NOT EXISTS assistance_distributions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
household_id INTEGER NOT NULL,
assistance_type VARCHAR(50) NOT NULL CHECK(assistance_type IN (\'pangan\', \'tunai\', \'kesehatan\', \'pendidikan\', \'usaha_mikro\', \'lainnya\')),
assistance_value REAL NOT NULL DEFAULT 0,
assistance_frequency VARCHAR(30) NOT NULL DEFAULT \'sekali\' CHECK(assistance_frequency IN (\'sekali\', \'mingguan\', \'bulanan\', \'triwulanan\', \'tahunan\', \'insidental\')),
id INT AUTO_INCREMENT PRIMARY KEY,
household_id INT NOT NULL,
assistance_type ENUM('pangan', 'tunai', 'kesehatan', 'pendidikan', 'usaha_mikro', 'lainnya') NOT NULL,
assistance_value DOUBLE NOT NULL DEFAULT 0,
assistance_frequency ENUM('sekali', 'mingguan', 'bulanan', 'triwulanan', 'tahunan', 'insidental') NOT NULL DEFAULT 'sekali',
distribution_date DATE NOT NULL,
delivery_status VARCHAR(30) NOT NULL DEFAULT \'pending\' CHECK(delivery_status IN (\'pending\', \'delivered\', \'failed\')),
delivered_by VARCHAR(255),
notes TEXT,
delivery_status ENUM('pending', 'delivered', 'failed') NOT NULL DEFAULT 'pending',
delivered_by VARCHAR(255) DEFAULT NULL,
notes TEXT DEFAULT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(household_id) REFERENCES households(id) ON DELETE CASCADE
)
');
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (household_id) REFERENCES households(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
");
// Create indexes for assistance distributions table
try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_assistance_household_id ON assistance_distributions(household_id)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_assistance_date ON assistance_distributions(distribution_date)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_assistance_status ON assistance_distributions(delivery_status)');
} catch (PDOException $e) {
// Indexes may already exist
}
$db->exec('CREATE INDEX idx_assistance_household_id ON assistance_distributions(household_id)');
} catch (PDOException $e) { /* Index may already exist */ }
try {
$db->exec('CREATE INDEX idx_assistance_date ON assistance_distributions(distribution_date)');
} 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
$db->exec('
$db->exec("
CREATE TABLE IF NOT EXISTS verification_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
household_id INTEGER NOT NULL,
verification_status VARCHAR(30) NOT NULL CHECK(verification_status IN (\'field_verified\', \'admin_approved\', \'rejected\', \'needs_review\')),
id INT AUTO_INCREMENT PRIMARY KEY,
household_id INT NOT NULL,
verification_status ENUM('field_verified', 'admin_approved', 'rejected', 'needs_review') NOT NULL,
verifier_name VARCHAR(255) NOT NULL,
verifier_role VARCHAR(100),
field_note TEXT,
evidence_photo_count INTEGER NOT NULL DEFAULT 0,
data_confidence_score REAL,
verifier_role VARCHAR(100) DEFAULT NULL,
field_note TEXT DEFAULT NULL,
evidence_photo_count INT NOT NULL DEFAULT 0,
data_confidence_score DOUBLE DEFAULT NULL,
verified_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
try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_verification_household_id ON verification_logs(household_id)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_verification_status ON verification_logs(verification_status)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_verification_verified_at ON verification_logs(verified_at)');
} catch (PDOException $e) {
// Indexes may already exist
}
$db->exec('CREATE INDEX idx_verification_household_id ON verification_logs(household_id)');
} catch (PDOException $e) { /* Index may already exist */ }
try {
$db->exec('CREATE INDEX idx_verification_status ON verification_logs(verification_status)');
} 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)
$db->exec('
// Create household scores table
$db->exec("
CREATE TABLE IF NOT EXISTS household_scores (
id INTEGER PRIMARY KEY AUTOINCREMENT,
household_id INTEGER NOT NULL UNIQUE,
skr REAL NOT NULL DEFAULT 0,
sag REAL NOT NULL DEFAULT 0,
spi REAL NOT NULL DEFAULT 0,
priority_level VARCHAR(30) NOT NULL DEFAULT \'low\' CHECK(priority_level IN (\'very_high\', \'high\', \'medium\', \'low\')),
score_version VARCHAR(30) NOT NULL DEFAULT \'v1\',
components_json TEXT,
id INT AUTO_INCREMENT PRIMARY KEY,
household_id INT NOT NULL UNIQUE,
skr DOUBLE NOT NULL DEFAULT 0,
sag DOUBLE NOT NULL DEFAULT 0,
spi DOUBLE NOT NULL DEFAULT 0,
priority_level ENUM('very_high', 'high', 'medium', 'low') NOT NULL DEFAULT 'low',
score_version VARCHAR(30) NOT NULL DEFAULT 'v1',
components_json TEXT DEFAULT NULL,
computed_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(household_id) REFERENCES households(id) ON DELETE CASCADE
)
');
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (household_id) REFERENCES households(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
");
// Create indexes for household_scores table
try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_household_scores_spi ON household_scores(spi DESC)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_household_scores_priority ON household_scores(priority_level)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_household_scores_computed_at ON household_scores(computed_at)');
} catch (PDOException $e) {
// Indexes may already exist
}
$db->exec('CREATE INDEX idx_household_scores_spi ON household_scores(spi)');
} catch (PDOException $e) { /* Index may already exist */ }
try {
$db->exec('CREATE INDEX idx_household_scores_priority ON household_scores(priority_level)');
} 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
$db->exec('
CREATE TABLE IF NOT EXISTS score_metadata (
key_name VARCHAR(100) PRIMARY KEY,
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
$db->exec('
$db->exec("
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
role VARCHAR(50) NOT NULL DEFAULT \'Petugas\' CHECK(role IN (\'Admin\', \'Petugas\', \'Pimpinan\')),
is_active BOOLEAN NOT NULL DEFAULT 1,
role ENUM('Admin', 'Petugas', 'Pimpinan') NOT NULL DEFAULT 'Petugas',
is_active TINYINT(1) NOT NULL DEFAULT 1,
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
try {
$db->exec('CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_users_username ON users(username)');
$db->exec('CREATE INDEX IF NOT EXISTS idx_users_role ON users(role)');
} catch (PDOException $e) {
// Indexes may already exist
}
$db->exec('CREATE INDEX idx_users_email ON users(email)');
} catch (PDOException $e) { /* Index may already exist */ }
try {
$db->exec('CREATE INDEX idx_users_username ON users(username)');
} 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
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) {
$adminPassword = password_hash('admin123', PASSWORD_BCRYPT, ['cost' => 12]);
$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'
];
$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) {
$metaStmt->execute([$key, $value]);
}
@@ -895,15 +831,55 @@ function syncNeedPointsToHouseholds($db) {
$updatedCount = 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();
$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);
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) {
$pointId = (int)$row['id'];
@@ -945,7 +921,13 @@ function syncNeedPointsToHouseholds($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);
$computed = 0;
@@ -1160,14 +1142,13 @@ function upsertHouseholdScore($db, $householdId, $scoreData) {
updated_at
)
VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT(household_id)
DO UPDATE SET
skr = excluded.skr,
sag = excluded.sag,
spi = excluded.spi,
priority_level = excluded.priority_level,
score_version = excluded.score_version,
components_json = excluded.components_json,
ON DUPLICATE KEY UPDATE
skr = VALUES(skr),
sag = VALUES(sag),
spi = VALUES(spi),
priority_level = VALUES(priority_level),
score_version = VALUES(score_version),
components_json = VALUES(components_json),
computed_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') {
$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);
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']);
+1 -1
View File
@@ -35,7 +35,7 @@ try {
}
$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(['population_reference', (string)$populationReference]);
+2 -2
View File
@@ -77,13 +77,13 @@ try {
// Insert Pontianak areas
$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 (?, ?, ?, ?)
');
$today = date('Y-m-d');
$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 (?, ?, ?, ?)
');