first commit
This commit is contained in:
@@ -0,0 +1,389 @@
|
||||
<?php
|
||||
|
||||
// ===============================
|
||||
// FWC Commercial License Import
|
||||
// ===============================
|
||||
|
||||
$options = getopt("", [
|
||||
"base-dir::",
|
||||
"commercial-dir::",
|
||||
"full-reload::",
|
||||
]);
|
||||
|
||||
$baseDir = normalizePath($options["base-dir"] ?? (__DIR__ . "/../../data/fwcdata"));
|
||||
$currentDir = normalizePath($options["commercial-dir"] ?? ($baseDir . "/licenses/current/commercial"));
|
||||
$fullReload = true; // true = truncate license table before importing all current files
|
||||
|
||||
if (isset($options["full-reload"])) {
|
||||
$fullReload = filter_var($options["full-reload"], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? true;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
$databaseConfig = getDatabaseConfig();
|
||||
$dbHost = $databaseConfig["host"];
|
||||
$dbName = $databaseConfig["name"];
|
||||
$dbUser = $databaseConfig["user"];
|
||||
$dbPass = $databaseConfig["pass"];
|
||||
|
||||
$pdo = new PDO("mysql:host={$dbHost};dbname={$dbName};charset=utf8mb4", $dbUser, $dbPass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::MYSQL_ATTR_LOCAL_INFILE => true]);
|
||||
|
||||
$files = glob($currentDir . DIRECTORY_SEPARATOR . "*.txt");
|
||||
|
||||
if (empty($files)) {
|
||||
throw new Exception("No .txt files found in {$currentDir}");
|
||||
}
|
||||
|
||||
$importBatchCode = createUuid();
|
||||
|
||||
echo "Import batch: {$importBatchCode}" . PHP_EOL;
|
||||
|
||||
if ($fullReload) {
|
||||
echo "Truncating fwc_commercial_licenses..." . PHP_EOL;
|
||||
$pdo->exec("TRUNCATE TABLE fwc_commercial_licenses");
|
||||
}
|
||||
|
||||
$hasFailures = false;
|
||||
|
||||
foreach ($files as $filePath) {
|
||||
|
||||
$sourceFile = basename($filePath);
|
||||
$fileSize = filesize($filePath);
|
||||
|
||||
echo "Importing {$sourceFile}..." . PHP_EOL;
|
||||
|
||||
$licenseCode = getLicenseCode($sourceFile);
|
||||
$importSerial = createImportRecord($pdo, $importBatchCode, $sourceFile, $licenseCode, $filePath, $fileSize);
|
||||
|
||||
try {
|
||||
|
||||
updateImportStatus($pdo, $importSerial, "running");
|
||||
|
||||
$beforeCount = getLicenseCount($pdo);
|
||||
|
||||
$loadedRows = loadFile($pdo, $filePath, $sourceFile, $importBatchCode);
|
||||
|
||||
$afterCount = getLicenseCount($pdo);
|
||||
$recordsImported = max(0, $afterCount - $beforeCount);
|
||||
|
||||
completeImport($pdo, $importSerial, $recordsImported);
|
||||
|
||||
echo "Loaded {$loadedRows} rows from {$sourceFile}" . PHP_EOL;
|
||||
} catch (Exception $e) {
|
||||
|
||||
$hasFailures = true;
|
||||
|
||||
failImport($pdo, $importSerial, $e->getMessage());
|
||||
|
||||
echo "ERROR importing {$sourceFile}: " . $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasFailures) {
|
||||
sendWebHookAlert("FWC commercial license reload completed with errors.", "Script Alert", "ff9900");
|
||||
echo "Import completed with errors." . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "Import complete." . PHP_EOL;
|
||||
sendWebHookAlert("FWC commercial license reload successful.");
|
||||
} catch (Exception $e) {
|
||||
|
||||
echo "Import failed: " . $e->getMessage() . PHP_EOL;
|
||||
sendWebHookAlert("FWC commercial license reload failed: " . $e->getMessage(), "Script Alert", "ff0000");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// =======================
|
||||
// Load File into MySQL
|
||||
// =======================
|
||||
|
||||
function loadFile(PDO $pdo, string $filePath, string $sourceFile, string $importBatchCode): int {
|
||||
|
||||
$filePathSql = $pdo->quote($filePath);
|
||||
$sourceFileSql = $pdo->quote($sourceFile);
|
||||
$licenseCodeSql = $pdo->quote(getLicenseCode($sourceFile));
|
||||
$importBatchCodeSql = $pdo->quote($importBatchCode);
|
||||
|
||||
$sql = "LOAD DATA LOCAL INFILE {$filePathSql}
|
||||
INTO TABLE fwc_commercial_licenses
|
||||
CHARACTER SET utf8mb4
|
||||
FIELDS TERMINATED BY '\\t'
|
||||
LINES TERMINATED BY '\\n'
|
||||
IGNORE 1 LINES
|
||||
|
||||
( @LicenseNumber,
|
||||
@ApplicantId,
|
||||
@FloridaResidency,
|
||||
@IssuedTo,
|
||||
@LicenseScope,
|
||||
@LicenseBeginDate,
|
||||
@LicenseExpireDate,
|
||||
@CompanyOrLastName,
|
||||
@FirstName,
|
||||
@MiddleInit,
|
||||
@Suffix,
|
||||
@EmailAddress,
|
||||
@County,
|
||||
@MailStreet1,
|
||||
@MailStreet2,
|
||||
@MailCity,
|
||||
@MailState,
|
||||
@MailZipCode,
|
||||
@MailZip4,
|
||||
@ApplicantPhone,
|
||||
@EndorsementNumber,
|
||||
@EndorsementExpirationDate,
|
||||
@TotalTagQty )
|
||||
SET
|
||||
commercial_license_source_file = {$sourceFileSql},
|
||||
commercial_license_code = {$licenseCodeSql},
|
||||
commercial_license_number = TRIM(@LicenseNumber),
|
||||
commercial_license_applicant_id = CASE
|
||||
WHEN TRIM(@ApplicantId) REGEXP '^[0-9]+$' THEN TRIM(@ApplicantId)
|
||||
ELSE NULL
|
||||
END,
|
||||
commercial_license_florida_residency = TRIM(@FloridaResidency),
|
||||
commercial_license_issued_to = TRIM(@IssuedTo),
|
||||
commercial_license_scope = TRIM(@LicenseScope),
|
||||
commercial_license_begin_date = COALESCE(STR_TO_DATE(NULLIF(TRIM(@LicenseBeginDate), ''), '%Y-%m-%d %H:%i:%s'),
|
||||
STR_TO_DATE(NULLIF(TRIM(@LicenseBeginDate), ''), '%Y-%m-%d')),
|
||||
commercial_license_expire_date = COALESCE(STR_TO_DATE(NULLIF(TRIM(@LicenseExpireDate), ''), '%Y-%m-%d %H:%i:%s'),
|
||||
STR_TO_DATE(NULLIF(TRIM(@LicenseExpireDate), ''), '%Y-%m-%d')),
|
||||
commercial_license_company_or_last_name = TRIM(@CompanyOrLastName),
|
||||
commercial_license_first_name = TRIM(@FirstName),
|
||||
commercial_license_middle_init = TRIM(@MiddleInit),
|
||||
commercial_license_suffix = TRIM(@Suffix),
|
||||
commercial_license_email_address = TRIM(@EmailAddress),
|
||||
commercial_license_county = TRIM(@County),
|
||||
commercial_license_mail_street1 = TRIM(@MailStreet1),
|
||||
commercial_license_mail_street2 = TRIM(@MailStreet2),
|
||||
commercial_license_mail_city = TRIM(@MailCity),
|
||||
commercial_license_mail_state = TRIM(@MailState),
|
||||
commercial_license_mail_zip_code = TRIM(@MailZipCode),
|
||||
commercial_license_mail_zip4 = TRIM(@MailZip4),
|
||||
commercial_license_applicant_phone = TRIM(@ApplicantPhone),
|
||||
commercial_license_endorsement_number = TRIM(@EndorsementNumber),
|
||||
commercial_license_endorsement_expiration_date = COALESCE(STR_TO_DATE(NULLIF(TRIM(@EndorsementExpirationDate), ''), '%Y-%m-%d %H:%i:%s'),
|
||||
STR_TO_DATE(NULLIF(TRIM(@EndorsementExpirationDate), ''), '%Y-%m-%d')),
|
||||
commercial_license_total_tag_qty = CASE
|
||||
WHEN TRIM(REPLACE(@TotalTagQty, '\\r', '')) REGEXP '^[0-9]+$' THEN TRIM(REPLACE(@TotalTagQty, '\\r', ''))
|
||||
ELSE NULL
|
||||
END,
|
||||
commercial_license_import_batch_code = {$importBatchCodeSql}";
|
||||
|
||||
return (int) $pdo->exec($sql);
|
||||
}
|
||||
|
||||
// ===============
|
||||
// Import Tracking
|
||||
// ===============
|
||||
|
||||
function createImportRecord(PDO $pdo, string $batchCode, string $sourceFile, string $licenseCode, string $filePath, int $fileSize): int {
|
||||
|
||||
$sql = " INSERT INTO fwc_commercial_license_imports (
|
||||
import_batch_code,
|
||||
source_file,
|
||||
license_code,
|
||||
file_path,
|
||||
file_size,
|
||||
import_status,
|
||||
started_at )
|
||||
|
||||
VALUES ( :import_batch_code,
|
||||
:source_file,
|
||||
:license_code,
|
||||
:file_path,
|
||||
:file_size,
|
||||
'queued',
|
||||
NOW())";
|
||||
|
||||
$statement = $pdo->prepare($sql);
|
||||
|
||||
$statement->execute([":import_batch_code" => $batchCode, ":source_file" => $sourceFile, ":license_code" => $licenseCode, ":file_path" => $filePath, ":file_size" => $fileSize]);
|
||||
|
||||
return (int) $pdo->lastInsertId();
|
||||
}
|
||||
|
||||
function updateImportStatus(PDO $pdo, int $importSerial, string $status): void {
|
||||
|
||||
$sql = "UPDATE fwc_commercial_license_imports
|
||||
SET import_status = :status
|
||||
WHERE import_serial = :import_serial";
|
||||
|
||||
$statement = $pdo->prepare($sql);
|
||||
|
||||
$statement->execute([":status" => $status, ":import_serial" => $importSerial]);
|
||||
}
|
||||
|
||||
function completeImport(PDO $pdo, int $importSerial, int $recordsImported): void {
|
||||
|
||||
$sql = "UPDATE fwc_commercial_license_imports
|
||||
SET import_status = 'complete',
|
||||
records_imported = :records_imported,
|
||||
completed_at = NOW()
|
||||
WHERE import_serial = :import_serial";
|
||||
|
||||
$statement = $pdo->prepare($sql);
|
||||
|
||||
$statement->execute([":records_imported" => $recordsImported, ":import_serial" => $importSerial]);
|
||||
}
|
||||
|
||||
function failImport(PDO $pdo, int $importSerial, string $message): void {
|
||||
|
||||
$sql = "UPDATE fwc_commercial_license_imports
|
||||
SET import_status = 'failed',
|
||||
error_message = :error_message,
|
||||
completed_at = NOW()
|
||||
WHERE import_serial = :import_serial";
|
||||
|
||||
$statement = $pdo->prepare($sql);
|
||||
|
||||
$statement->execute([":error_message" => $message, ":import_serial" => $importSerial]);
|
||||
}
|
||||
|
||||
// =======
|
||||
// Helpers
|
||||
// =======
|
||||
|
||||
function getLicenseCount(PDO $pdo): int {
|
||||
|
||||
return (int) $pdo->query("SELECT COUNT(*) FROM fwc_commercial_licenses")->fetchColumn();
|
||||
}
|
||||
|
||||
function getLicenseCode(string $sourceFile): string {
|
||||
|
||||
$fileName = strtolower(pathinfo($sourceFile, PATHINFO_FILENAME));
|
||||
|
||||
$licenseCodePatterns = [
|
||||
"closedseasonspinylobsterplaneandvessel" => "CSV",
|
||||
"closedseasonspinylobster" => "CS",
|
||||
"depredationpermit" => "DP",
|
||||
"freshwatercommercialfishing" => "FCL",
|
||||
"nonresidentfreshwaterretaildealers" => "FRD",
|
||||
"nonresidentfreshwaterwholesalebuyers" => "FWB",
|
||||
"nonresidentfreshwaterwholesaledealers" => "FWD",
|
||||
"stjohnsriverliveshrimp" => "LS",
|
||||
"retailcentral" => "RC",
|
||||
"residentfreshwaterfishandfrogdealers" => "RFD",
|
||||
"retailother" => "RO",
|
||||
"sardinelikefish" => "SL",
|
||||
"saltwaterproducts" => "SP",
|
||||
"wholesaledealer" => "WD",
|
||||
];
|
||||
|
||||
foreach ($licenseCodePatterns as $pattern => $licenseCode) {
|
||||
if (strpos($fileName, $pattern) !== false) {
|
||||
return $licenseCode;
|
||||
}
|
||||
}
|
||||
|
||||
return strtoupper(substr($fileName, 0, 10));
|
||||
}
|
||||
|
||||
function createUuid(): string {
|
||||
|
||||
$data = random_bytes(16);
|
||||
|
||||
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
|
||||
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
|
||||
|
||||
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||
}
|
||||
|
||||
function normalizePath(string $path): string {
|
||||
|
||||
$realPath = realpath($path);
|
||||
|
||||
return $realPath === false ? rtrim($path, DIRECTORY_SEPARATOR) : $realPath;
|
||||
}
|
||||
|
||||
function getDatabaseConfig(): array {
|
||||
|
||||
$config = [
|
||||
"host" => getenv("FWC_DB_HOST") ?: "",
|
||||
"name" => getenv("FWC_DB_NAME") ?: "",
|
||||
"user" => getenv("FWC_DB_USER") ?: "",
|
||||
"pass" => getenv("FWC_DB_PASS") ?: "",
|
||||
];
|
||||
|
||||
if ($config["host"] !== "" && $config["name"] !== "" && $config["user"] !== "" && $config["pass"] !== "") {
|
||||
return $config;
|
||||
}
|
||||
|
||||
$settingsPath = __DIR__ . "/../../xml/settings.xml";
|
||||
|
||||
if (!file_exists($settingsPath)) {
|
||||
throw new Exception("Database settings not found. Set FWC_DB_HOST, FWC_DB_NAME, FWC_DB_USER, and FWC_DB_PASS or create {$settingsPath}");
|
||||
}
|
||||
|
||||
$settings = new SimpleXMLElement(file_get_contents($settingsPath));
|
||||
|
||||
$config = [
|
||||
"host" => trim((string) $settings->DatabaseServer),
|
||||
"name" => trim((string) $settings->DatabaseName),
|
||||
"user" => trim((string) $settings->DatabaseUser),
|
||||
"pass" => trim((string) $settings->DatabasePassword),
|
||||
];
|
||||
|
||||
foreach ($config as $key => $value) {
|
||||
if ($value === "") {
|
||||
throw new Exception("Database setting {$key} is missing in {$settingsPath}");
|
||||
}
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
function sendWebHookAlert(string $message, string $title = "Script Alert", string $alerttype = "1fff00"): void {
|
||||
|
||||
$webhookUrl = getenv("FWC_WEBHOOK_URL") ?: "";
|
||||
|
||||
if ($webhookUrl === "") {
|
||||
$settingsPath = __DIR__ . "/../../xml/settings.xml";
|
||||
|
||||
if (file_exists($settingsPath)) {
|
||||
$settings = new SimpleXMLElement(file_get_contents($settingsPath));
|
||||
$webhookUrl = trim((string) $settings->DiscordWebHookURL);
|
||||
}
|
||||
}
|
||||
|
||||
if (trim($webhookUrl) === "") {
|
||||
echo "Webhook alert skipped: FWC_WEBHOOK_URL is not set." . PHP_EOL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!function_exists("curl_init")) {
|
||||
echo "Webhook alert skipped: PHP curl extension is not available." . PHP_EOL;
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'embeds' => [[
|
||||
'title' => $title,
|
||||
'description' => $message,
|
||||
'color' => hexdec($alerttype),
|
||||
'footer' => [
|
||||
'text' => 'OSITS System Notification',
|
||||
],
|
||||
'timestamp' => date('c'),
|
||||
]]
|
||||
];
|
||||
|
||||
$jsonData = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
$ch = curl_init($webhookUrl);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if ($response === false) {
|
||||
echo "Webhook alert failed: " . curl_error($ch) . PHP_EOL;
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
// ===============================
|
||||
// FWC Recreational License Import
|
||||
// ===============================
|
||||
|
||||
$options = getopt("", [
|
||||
"base-dir::",
|
||||
"recreational-dir::",
|
||||
"full-reload::",
|
||||
]);
|
||||
|
||||
$baseDir = normalizePath($options["base-dir"] ?? (__DIR__ . "/../../data/fwcdata"));
|
||||
$currentDir = normalizePath($options["recreational-dir"] ?? ($baseDir . "/licenses/current/recreational"));
|
||||
$fullReload = true; // true = truncate license table before importing all current files
|
||||
|
||||
if (isset($options["full-reload"])) {
|
||||
$fullReload = filter_var($options["full-reload"], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? true;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
$databaseConfig = getDatabaseConfig();
|
||||
$dbHost = $databaseConfig["host"];
|
||||
$dbName = $databaseConfig["name"];
|
||||
$dbUser = $databaseConfig["user"];
|
||||
$dbPass = $databaseConfig["pass"];
|
||||
|
||||
$pdo = new PDO("mysql:host={$dbHost};dbname={$dbName};charset=utf8mb4", $dbUser, $dbPass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::MYSQL_ATTR_LOCAL_INFILE => true]);
|
||||
|
||||
$files = glob($currentDir . DIRECTORY_SEPARATOR . "*.txt");
|
||||
|
||||
if (empty($files)) {
|
||||
throw new Exception("No .txt files found in {$currentDir}");
|
||||
}
|
||||
|
||||
$importBatchCode = createUuid();
|
||||
|
||||
echo "Import batch: {$importBatchCode}" . PHP_EOL;
|
||||
|
||||
if ($fullReload) {
|
||||
echo "Truncating fwc_recreational_licenses..." . PHP_EOL;
|
||||
$pdo->exec("TRUNCATE TABLE fwc_recreational_licenses");
|
||||
}
|
||||
|
||||
$hasFailures = false;
|
||||
|
||||
foreach ($files as $filePath) {
|
||||
|
||||
$sourceFile = basename($filePath);
|
||||
$fileSize = filesize($filePath);
|
||||
|
||||
echo "Importing {$sourceFile}..." . PHP_EOL;
|
||||
|
||||
$importSerial = createImportRecord($pdo, $importBatchCode, $sourceFile, $filePath, $fileSize);
|
||||
|
||||
try {
|
||||
|
||||
updateImportStatus($pdo, $importSerial, "running");
|
||||
|
||||
$beforeCount = getLicenseCount($pdo);
|
||||
|
||||
$loadedRows = loadFile($pdo, $filePath, $sourceFile, $importBatchCode);
|
||||
|
||||
purgeOutOfStateRecords($pdo);
|
||||
|
||||
$afterCount = getLicenseCount($pdo);
|
||||
$recordsImported = max(0, $afterCount - $beforeCount);
|
||||
|
||||
completeImport($pdo, $importSerial, $recordsImported);
|
||||
|
||||
echo "Loaded {$loadedRows} rows; retained {$recordsImported} Florida records from {$sourceFile}" . PHP_EOL;
|
||||
} catch (Exception $e) {
|
||||
|
||||
$hasFailures = true;
|
||||
|
||||
failImport($pdo, $importSerial, $e->getMessage());
|
||||
|
||||
echo "ERROR importing {$sourceFile}: " . $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasFailures) {
|
||||
sendWebHookAlert("FWC recreational license reload completed with errors.", "Script Alert", "ff9900");
|
||||
echo "Import completed with errors." . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "Import complete." . PHP_EOL;
|
||||
sendWebHookAlert("FWC recreational license reload successful.");
|
||||
} catch (Exception $e) {
|
||||
|
||||
echo "Import failed: " . $e->getMessage() . PHP_EOL;
|
||||
sendWebHookAlert("FWC recreational license reload failed: " . $e->getMessage(), "Script Alert", "ff0000");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// =======================
|
||||
// Load File into MySQL
|
||||
// =======================
|
||||
|
||||
function loadFile(PDO $pdo, string $filePath, string $sourceFile, string $importBatchCode): int {
|
||||
|
||||
$filePathSql = $pdo->quote($filePath);
|
||||
$sourceFileSql = $pdo->quote($sourceFile);
|
||||
$importBatchCodeSql = $pdo->quote($importBatchCode);
|
||||
|
||||
$sql = "LOAD DATA LOCAL INFILE {$filePathSql}
|
||||
INTO TABLE fwc_recreational_licenses
|
||||
CHARACTER SET utf8mb4
|
||||
FIELDS TERMINATED BY '\\t'
|
||||
LINES TERMINATED BY '\\n'
|
||||
IGNORE 1 LINES
|
||||
|
||||
( @lastName,
|
||||
@firstName,
|
||||
@middleName,
|
||||
@street1,
|
||||
@city,
|
||||
@state,
|
||||
@zipCode,
|
||||
@phoneNumber,
|
||||
@emailAddress,
|
||||
@Gender,
|
||||
@Ethnicity,
|
||||
@LicenseType,
|
||||
@LicenseExpireDate,
|
||||
@LicenseStartDate,
|
||||
@AgeAtTimeOfRun,
|
||||
@County )
|
||||
SET
|
||||
license_source_file = {$sourceFileSql},
|
||||
license_last_name = TRIM(@lastName),
|
||||
license_first_name = TRIM(@firstName),
|
||||
license_middle_name = TRIM(@middleName),
|
||||
license_street1 = TRIM(@street1),
|
||||
license_city = TRIM(@city),
|
||||
license_state = TRIM(@state),
|
||||
license_zip_code = TRIM(@zipCode),
|
||||
license_phone_number = TRIM(@phoneNumber),
|
||||
license_email_address = TRIM(@emailAddress),
|
||||
license_gender = TRIM(@Gender),
|
||||
license_ethnicity = TRIM(@Ethnicity),
|
||||
license_type = TRIM(@LicenseType),
|
||||
license_expire_date = COALESCE(STR_TO_DATE(NULLIF(TRIM(@LicenseExpireDate), ''), '%Y-%m-%d %H:%i:%s'),
|
||||
STR_TO_DATE(NULLIF(TRIM(@LicenseExpireDate), ''), '%Y-%m-%d')),
|
||||
license_start_date = COALESCE(STR_TO_DATE(NULLIF(TRIM(@LicenseStartDate), ''), '%Y-%m-%d %H:%i:%s'),
|
||||
STR_TO_DATE(NULLIF(TRIM(@LicenseStartDate), ''), '%Y-%m-%d')),
|
||||
license_age_at_time_of_run = CASE
|
||||
WHEN TRIM(@AgeAtTimeOfRun) REGEXP '^[0-9]+$' THEN TRIM(@AgeAtTimeOfRun)
|
||||
ELSE NULL
|
||||
END,
|
||||
license_county = TRIM(REPLACE(@County, '\\r', '')),
|
||||
import_batch_code = {$importBatchCodeSql}";
|
||||
|
||||
return (int) $pdo->exec($sql);
|
||||
}
|
||||
|
||||
// ===============
|
||||
// Import Tracking
|
||||
// ===============
|
||||
|
||||
function createImportRecord(PDO $pdo, string $batchCode, string $sourceFile, string $filePath, int $fileSize): int {
|
||||
|
||||
$sql = " INSERT INTO fwc_recreational_license_imports (
|
||||
import_batch_code,
|
||||
source_file,
|
||||
file_path,
|
||||
file_size,
|
||||
import_status,
|
||||
started_at )
|
||||
|
||||
VALUES ( :import_batch_code,
|
||||
:source_file,
|
||||
:file_path,
|
||||
:file_size,
|
||||
'queued',
|
||||
NOW())";
|
||||
|
||||
$statement = $pdo->prepare($sql);
|
||||
|
||||
$statement->execute([":import_batch_code" => $batchCode, ":source_file" => $sourceFile, ":file_path" => $filePath, ":file_size" => $fileSize]);
|
||||
|
||||
return (int) $pdo->lastInsertId();
|
||||
}
|
||||
|
||||
function updateImportStatus(PDO $pdo, int $importSerial, string $status): void {
|
||||
|
||||
$sql = "UPDATE fwc_recreational_license_imports
|
||||
SET import_status = :status
|
||||
WHERE import_serial = :import_serial";
|
||||
|
||||
$statement = $pdo->prepare($sql);
|
||||
|
||||
$statement->execute([":status" => $status, ":import_serial" => $importSerial]);
|
||||
}
|
||||
|
||||
function completeImport(PDO $pdo, int $importSerial, int $recordsImported): void {
|
||||
|
||||
$sql = "UPDATE fwc_recreational_license_imports
|
||||
SET import_status = 'complete',
|
||||
records_imported = :records_imported,
|
||||
completed_at = NOW()
|
||||
WHERE import_serial = :import_serial";
|
||||
|
||||
$statement = $pdo->prepare($sql);
|
||||
|
||||
$statement->execute([":records_imported" => $recordsImported, ":import_serial" => $importSerial]);
|
||||
}
|
||||
|
||||
function failImport(PDO $pdo, int $importSerial, string $message): void {
|
||||
|
||||
$sql = "UPDATE fwc_recreational_license_imports
|
||||
SET import_status = 'failed',
|
||||
error_message = :error_message,
|
||||
completed_at = NOW()
|
||||
WHERE import_serial = :import_serial";
|
||||
|
||||
$statement = $pdo->prepare($sql);
|
||||
|
||||
$statement->execute([":error_message" => $message, ":import_serial" => $importSerial]);
|
||||
}
|
||||
|
||||
function purgeOutOfStateRecords(PDO $pdo): void {
|
||||
|
||||
$sql = "delete from fwc_recreational_licenses where license_state != 'FL'";
|
||||
|
||||
$statement = $pdo->prepare($sql);
|
||||
|
||||
$statement->execute();
|
||||
}
|
||||
|
||||
// =======
|
||||
// Helpers
|
||||
// =======
|
||||
|
||||
function getLicenseCount(PDO $pdo): int {
|
||||
|
||||
return (int) $pdo->query("SELECT COUNT(*) FROM fwc_recreational_licenses")->fetchColumn();
|
||||
}
|
||||
|
||||
function createUuid(): string {
|
||||
|
||||
$data = random_bytes(16);
|
||||
|
||||
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
|
||||
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
|
||||
|
||||
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||
}
|
||||
|
||||
function normalizePath(string $path): string {
|
||||
|
||||
$realPath = realpath($path);
|
||||
|
||||
return $realPath === false ? rtrim($path, DIRECTORY_SEPARATOR) : $realPath;
|
||||
}
|
||||
|
||||
function getDatabaseConfig(): array {
|
||||
|
||||
$config = [
|
||||
"host" => getenv("FWC_DB_HOST") ?: "",
|
||||
"name" => getenv("FWC_DB_NAME") ?: "",
|
||||
"user" => getenv("FWC_DB_USER") ?: "",
|
||||
"pass" => getenv("FWC_DB_PASS") ?: "",
|
||||
];
|
||||
|
||||
if ($config["host"] !== "" && $config["name"] !== "" && $config["user"] !== "" && $config["pass"] !== "") {
|
||||
return $config;
|
||||
}
|
||||
|
||||
$settingsPath = __DIR__ . "/../../xml/settings.xml";
|
||||
|
||||
if (!file_exists($settingsPath)) {
|
||||
throw new Exception("Database settings not found. Set FWC_DB_HOST, FWC_DB_NAME, FWC_DB_USER, and FWC_DB_PASS or create {$settingsPath}");
|
||||
}
|
||||
|
||||
$settings = new SimpleXMLElement(file_get_contents($settingsPath));
|
||||
|
||||
$config = [
|
||||
"host" => trim((string) $settings->DatabaseServer),
|
||||
"name" => trim((string) $settings->DatabaseName),
|
||||
"user" => trim((string) $settings->DatabaseUser),
|
||||
"pass" => trim((string) $settings->DatabasePassword),
|
||||
];
|
||||
|
||||
foreach ($config as $key => $value) {
|
||||
if ($value === "") {
|
||||
throw new Exception("Database setting {$key} is missing in {$settingsPath}");
|
||||
}
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
function sendWebHookAlert(string $message, string $title = "Script Alert", string $alerttype = "1fff00"): void {
|
||||
|
||||
$webhookUrl = getenv("FWC_WEBHOOK_URL") ?: "";
|
||||
|
||||
if ($webhookUrl === "") {
|
||||
$settingsPath = __DIR__ . "/../../xml/settings.xml";
|
||||
|
||||
if (file_exists($settingsPath)) {
|
||||
$settings = new SimpleXMLElement(file_get_contents($settingsPath));
|
||||
$webhookUrl = trim((string) $settings->DiscordWebHookURL);
|
||||
}
|
||||
}
|
||||
|
||||
if (trim($webhookUrl) === "") {
|
||||
echo "Webhook alert skipped: FWC_WEBHOOK_URL is not set." . PHP_EOL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!function_exists("curl_init")) {
|
||||
echo "Webhook alert skipped: PHP curl extension is not available." . PHP_EOL;
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'embeds' => [[
|
||||
'title' => $title,
|
||||
'description' => $message,
|
||||
'color' => hexdec($alerttype),
|
||||
'footer' => [
|
||||
'text' => 'OSITS System Notification',
|
||||
],
|
||||
'timestamp' => date('c'),
|
||||
]]
|
||||
];
|
||||
|
||||
$jsonData = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
$ch = curl_init($webhookUrl);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if ($response === false) {
|
||||
echo "Webhook alert failed: " . curl_error($ch) . PHP_EOL;
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
|
||||
// ====================
|
||||
// Import Voter Records
|
||||
// ====================
|
||||
|
||||
class Import_VoterRecords {
|
||||
|
||||
// Variables
|
||||
|
||||
private $log = "";
|
||||
private $settings = "";
|
||||
private $connection = "";
|
||||
|
||||
// ==================
|
||||
// Class Constructor.
|
||||
// ==================
|
||||
|
||||
public function __construct() {
|
||||
|
||||
// Open the Log
|
||||
|
||||
$this->log = fopen("../scripts/import_voter_records.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 New DEC-DR 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 Record Import Process");
|
||||
|
||||
$this->importVoterRecords();
|
||||
|
||||
$this->log("Voter Record Import Process Complete");
|
||||
}
|
||||
|
||||
// ====================
|
||||
// Import Voter Records
|
||||
// ====================
|
||||
|
||||
private function importVoterRecords() {
|
||||
|
||||
$this->log("Processing Voter Records");
|
||||
|
||||
$voter_record_file_path = '/data/ositssrc/projects/votervue_raw_data/20251210_Voter_Detail_20251210';
|
||||
$voter_history_files = glob("{$voter_record_file_path}/*.txt");
|
||||
sort($voter_history_files);
|
||||
|
||||
$SQL = "INSERT INTO voters
|
||||
( voter_county_code,
|
||||
voter_id,
|
||||
voter_name_last,
|
||||
voter_name_suffix,
|
||||
voter_name_first,
|
||||
voter_name_middle,
|
||||
voter_public_records_exempt,
|
||||
voter_residence_address_1,
|
||||
voter_residence_address_2,
|
||||
voter_residence_city,
|
||||
voter_residence_state,
|
||||
voter_residence_zipcode,
|
||||
voter_mailing_address_1,
|
||||
voter_mailing_address_2,
|
||||
voter_mailing_address_3,
|
||||
voter_mailing_city,
|
||||
voter_mailing_state,
|
||||
voter_mailing_zipcode,
|
||||
voter_mailing_country,
|
||||
voter_gender,
|
||||
voter_race,
|
||||
voter_birth_date,
|
||||
voter_registration_date,
|
||||
voter_party_affiliation,
|
||||
voter_precinct,
|
||||
voter_precinct_group,
|
||||
voter_precinct_split,
|
||||
voter_precinct_suffix,
|
||||
voter_voter_status,
|
||||
voter_congressional_district,
|
||||
voter_house_district,
|
||||
voter_senate_district,
|
||||
voter_county_commission_district,
|
||||
voter_school_board_district,
|
||||
voter_daytime_area_code,
|
||||
voter_daytime_phone_number,
|
||||
voter_daytime_phone_extension,
|
||||
voter_email_address )
|
||||
VALUES
|
||||
( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||
?, ?, ?, ?, ? )";
|
||||
|
||||
$statement = $this->connection->prepare($SQL);
|
||||
|
||||
$counter = 0;
|
||||
|
||||
foreach ($voter_history_files as $file) {
|
||||
|
||||
$this->log("Importing file: {$file}");
|
||||
|
||||
$voterhistory = fopen($file, 'r');
|
||||
|
||||
if ($voterhistory === false) {
|
||||
$this->log("Unable to open {$file}");
|
||||
echo "❌ Unable to open {$file}\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
$lineNum = 0;
|
||||
|
||||
while (($line = fgets($voterhistory)) !== false) {
|
||||
$lineNum++;
|
||||
|
||||
$line = rtrim($line, "\r\n");
|
||||
if ($line === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$cols = explode("\t", $line);
|
||||
|
||||
if (count($cols) < 38) {
|
||||
$this->log("Skipped malformed line {$lineNum} in {$file} (cols=" . count($cols) . ")");
|
||||
continue;
|
||||
}
|
||||
|
||||
[$county_code, $voter_id, $name_last, $name_suffix, $name_first, $name_middle, $public_exempt, $res_addr1, $res_addr2, $res_city, $res_state, $res_zip,
|
||||
$mail_addr1, $mail_addr2, $mail_addr3, $mail_city, $mail_state, $mail_zip, $mail_country, $gender, $race, $birth_date, $reg_date, $party, $precinct,
|
||||
$precinct_group, $precinct_split, $precinct_suffix, $status, $cd, $hd, $sd, $ccd, $sbd, $area, $phone, $ext, $email] = $cols;
|
||||
|
||||
try {
|
||||
|
||||
$statement->execute([
|
||||
$this->normalize($county_code),
|
||||
$this->normalize($voter_id),
|
||||
$this->normalize($name_last),
|
||||
$this->normalize($name_suffix),
|
||||
$this->normalize($name_first),
|
||||
$this->normalize($name_middle),
|
||||
$this->normalize($public_exempt),
|
||||
$this->normalize($res_addr1),
|
||||
$this->normalize($res_addr2),
|
||||
$this->normalize($res_city),
|
||||
$this->normalize($res_state),
|
||||
$this->normalize($res_zip),
|
||||
$this->normalize($mail_addr1),
|
||||
$this->normalize($mail_addr2),
|
||||
$this->normalize($mail_addr3),
|
||||
$this->normalize($mail_city),
|
||||
$this->normalize($mail_state),
|
||||
$this->normalize($mail_zip),
|
||||
$this->normalize($mail_country),
|
||||
$this->normalize($gender),
|
||||
$this->normalize($race),
|
||||
$this->parseDate($birth_date),
|
||||
$this->parseDate($reg_date),
|
||||
$this->normalize($party),
|
||||
$this->normalize($precinct),
|
||||
$this->normalize($precinct_group),
|
||||
$this->normalize($precinct_split),
|
||||
$this->normalize($precinct_suffix),
|
||||
$this->normalize($status),
|
||||
$this->normalize($cd),
|
||||
$this->normalize($hd),
|
||||
$this->normalize($sd),
|
||||
$this->normalize($ccd),
|
||||
$this->normalize($sbd),
|
||||
$this->normalize($area),
|
||||
$this->normalize($phone),
|
||||
$this->normalize($ext),
|
||||
$this->normalize($email)
|
||||
]);
|
||||
|
||||
$counter++;
|
||||
|
||||
if (($counter % 10) === 0) {
|
||||
$this->log("Processed {$counter} records...");
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$this->log("Insert failed {$file}:{$lineNum} - " . $e->getMessage());
|
||||
echo "❌ Insert failed {$file}:{$lineNum} → {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
|
||||
fclose($voterhistory);
|
||||
}
|
||||
|
||||
$this->log("Completed voter 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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user