Upload files to "api"
This commit is contained in:
+267
-286
@@ -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
@@ -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']);
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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 (?, ?, ?, ?)
|
||||
');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user