226 lines
6.1 KiB
PHP
226 lines
6.1 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
// ====================
|
||
|
|
// Import Voter History
|
||
|
|
// ====================
|
||
|
|
|
||
|
|
class Import_VoterHistory {
|
||
|
|
|
||
|
|
// Variables
|
||
|
|
|
||
|
|
private $log = "";
|
||
|
|
private $settings = "";
|
||
|
|
private $connection = "";
|
||
|
|
|
||
|
|
// ==================
|
||
|
|
// Class Constructor.
|
||
|
|
// ==================
|
||
|
|
|
||
|
|
public function __construct() {
|
||
|
|
|
||
|
|
// Open the Log
|
||
|
|
|
||
|
|
$this->log = fopen("../scripts/import_voter_history.log", "w+");
|
||
|
|
|
||
|
|
if ($this->log === false) {
|
||
|
|
|
||
|
|
error_log("VoterVue : Unable to open log file");
|
||
|
|
exit();
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->log(str_repeat("*", 50));
|
||
|
|
|
||
|
|
// Get Settings
|
||
|
|
|
||
|
|
$this->settings = new SimpleXMLElement((file_exists("../xml/settings.xml") ? file_get_contents("../xml/settings.xml") : "<settings/>"));
|
||
|
|
|
||
|
|
if ($this->settings->count() === 0) {
|
||
|
|
|
||
|
|
error_log("VoterVue : Unable to initialize settings");
|
||
|
|
exit();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Open Database Connections
|
||
|
|
|
||
|
|
$this->connection = $this->connect();
|
||
|
|
}
|
||
|
|
|
||
|
|
// ==============================================
|
||
|
|
// Return a VoterVue Database Database Connection
|
||
|
|
// ==============================================
|
||
|
|
|
||
|
|
public function connect() {
|
||
|
|
|
||
|
|
try {
|
||
|
|
|
||
|
|
$host = (string) $this->settings->DatabaseServer;
|
||
|
|
$database = (string) $this->settings->DatabaseName;
|
||
|
|
$user = (string) $this->settings->DatabaseUser;
|
||
|
|
$password = (string) $this->settings->DatabasePassword;
|
||
|
|
|
||
|
|
$connection = new PDO("mysql:host={$host};dbname={$database};charset=utf8", $user, $password);
|
||
|
|
|
||
|
|
$connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
|
||
|
|
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||
|
|
} catch (PDOException | Exception $e) {
|
||
|
|
|
||
|
|
trigger_error($e->getMessage());
|
||
|
|
}
|
||
|
|
|
||
|
|
return $connection;
|
||
|
|
}
|
||
|
|
|
||
|
|
// =============================
|
||
|
|
// Log a message to the Log File
|
||
|
|
// =============================
|
||
|
|
|
||
|
|
private function log($message = "") {
|
||
|
|
|
||
|
|
fwrite($this->log, date("Y-m-d H:i A") . " : " . "{$message} \n");
|
||
|
|
}
|
||
|
|
|
||
|
|
// ===================
|
||
|
|
// Process Parcel Data
|
||
|
|
// ===================
|
||
|
|
|
||
|
|
public function process() {
|
||
|
|
|
||
|
|
$this->log("Starting Voter History Import Process");
|
||
|
|
|
||
|
|
$this->importVoterHistory();
|
||
|
|
|
||
|
|
$this->log("Voter History Import Process Completed");
|
||
|
|
}
|
||
|
|
|
||
|
|
// ====================
|
||
|
|
// Import Voter History
|
||
|
|
// ====================
|
||
|
|
|
||
|
|
private function importVoterHistory() {
|
||
|
|
|
||
|
|
$this->log("Processing Voter History Records");
|
||
|
|
|
||
|
|
$path = '/data/ositssrc/projects/votervue_raw_data/20251210_Voter_History_20251210';
|
||
|
|
$files = glob("{$path}/*.txt");
|
||
|
|
sort($files);
|
||
|
|
|
||
|
|
$SQL = "INSERT IGNORE INTO voterhistory
|
||
|
|
(
|
||
|
|
voterhistory_county_code,
|
||
|
|
voterhistory_voter_id,
|
||
|
|
voterhistory_election_date,
|
||
|
|
voterhistory_election_type,
|
||
|
|
voterhistory_history_code
|
||
|
|
)
|
||
|
|
VALUES ( ?, ?, ?, ?, ? )";
|
||
|
|
|
||
|
|
$statement = $this->connection->prepare($SQL);
|
||
|
|
|
||
|
|
$counter = 0;
|
||
|
|
$batchCounter = 0;
|
||
|
|
$batchSize = 50000;
|
||
|
|
|
||
|
|
foreach ($files as $file) {
|
||
|
|
|
||
|
|
$this->log("Importing history file: {$file}");
|
||
|
|
|
||
|
|
$fh = fopen($file, 'r');
|
||
|
|
|
||
|
|
if ($fh === false) {
|
||
|
|
$this->log("Unable to open {$file}");
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
$lineNum = 0;
|
||
|
|
|
||
|
|
try {
|
||
|
|
|
||
|
|
// Start transaction for this file
|
||
|
|
$this->connection->beginTransaction();
|
||
|
|
|
||
|
|
while (($line = fgets($fh)) !== false) {
|
||
|
|
$lineNum++;
|
||
|
|
|
||
|
|
$line = rtrim($line, "\r\n");
|
||
|
|
if ($line === '') {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
$cols = explode("\t", $line);
|
||
|
|
|
||
|
|
if (count($cols) < 5) {
|
||
|
|
$this->log("Skipped malformed line {$lineNum} in {$file} (cols=" . count($cols) . ")");
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
[$county_code, $voter_id, $election_date, $election_type, $history_code] = $cols;
|
||
|
|
|
||
|
|
$statement->execute([
|
||
|
|
$this->normalize($county_code),
|
||
|
|
$this->normalize($voter_id),
|
||
|
|
$this->parseDate($election_date),
|
||
|
|
$this->normalize($election_type),
|
||
|
|
$this->normalize($history_code)
|
||
|
|
]);
|
||
|
|
|
||
|
|
$counter++;
|
||
|
|
$batchCounter++;
|
||
|
|
|
||
|
|
// Commit every batchSize rows
|
||
|
|
if ($batchCounter >= $batchSize) {
|
||
|
|
$this->connection->commit();
|
||
|
|
$this->connection->beginTransaction();
|
||
|
|
$batchCounter = 0;
|
||
|
|
|
||
|
|
$this->log("Processed {$counter} history records...");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Final commit for this file
|
||
|
|
$this->connection->commit();
|
||
|
|
$batchCounter = 0;
|
||
|
|
} catch (Throwable $e) {
|
||
|
|
|
||
|
|
// Roll back any open transaction for this file
|
||
|
|
if ($this->connection->inTransaction()) {
|
||
|
|
$this->connection->rollBack();
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->log("Insert failed {$file}:{$lineNum} - " . $e->getMessage());
|
||
|
|
}
|
||
|
|
|
||
|
|
fclose($fh);
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->log("Completed voter history import. Total records processed: {$counter}");
|
||
|
|
}
|
||
|
|
|
||
|
|
// =================================
|
||
|
|
// Return a $this->normalized String
|
||
|
|
// =================================
|
||
|
|
|
||
|
|
private function normalize(?string $value): ?string {
|
||
|
|
|
||
|
|
$value = trim((string) $value);
|
||
|
|
|
||
|
|
return ($value === '' || $value === '*') ? null : $value;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ====================
|
||
|
|
// Return a Parsed Date
|
||
|
|
// ====================
|
||
|
|
|
||
|
|
private function parseDate(?string $value): ?string {
|
||
|
|
|
||
|
|
$value = $this->normalize($value);
|
||
|
|
if ($value === null) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
$dt = DateTime::createFromFormat('m/d/Y', $value);
|
||
|
|
|
||
|
|
return $dt ? $dt->format('Y-m-d') : null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
?>
|