584 lines
20 KiB
PHP
584 lines
20 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
$basePath = realpath(__DIR__ . '/..');
|
|
require_once $basePath . '/vendor/autoload.php';
|
|
|
|
use PHPMailer\PHPMailer\PHPMailer;
|
|
use PHPMailer\PHPMailer\Exception;
|
|
|
|
// =====================
|
|
// Process_ImportRequest
|
|
// =====================
|
|
|
|
class Process_ImportRequest {
|
|
|
|
// Variables
|
|
|
|
private $log = "";
|
|
private $settings = "";
|
|
private $connection = "";
|
|
|
|
// ==================
|
|
// Class Constructor.
|
|
// ==================
|
|
|
|
public function __construct() {
|
|
|
|
$basePath = realpath(__DIR__ . '/..');
|
|
$logfile = $basePath . "/scripts/importrequests.log";
|
|
|
|
// ==========================
|
|
// Log Rotation (50 MB limit)
|
|
// ==========================
|
|
|
|
$maxLogSize = 50 * 1024 * 1024; // 50 MB
|
|
|
|
if (file_exists($logfile) && filesize($logfile) >= $maxLogSize) {
|
|
|
|
$rotated = $logfile . '-';
|
|
rename($logfile, $rotated);
|
|
}
|
|
|
|
$this->log = fopen($logfile, "a");
|
|
|
|
if ($this->log === false) {
|
|
|
|
error_log("Process_ImportRequest : Unable to open log file");
|
|
exit();
|
|
}
|
|
|
|
$this->log(str_repeat("*", 50));
|
|
|
|
// Get Settings
|
|
|
|
$this->settings = new SimpleXMLElement((file_exists($basePath . "/xml/settings.xml") ? file_get_contents($basePath . "/xml/settings.xml") : "<settings/>"));
|
|
|
|
if ($this->settings->count() === 0) {
|
|
|
|
error_log("Process_ImportRequest : Unable to initialize settings");
|
|
exit();
|
|
}
|
|
|
|
// Open Database Connections
|
|
|
|
$this->connection = $this->connect();
|
|
}
|
|
|
|
// ================================================
|
|
// Return a New DEC-DR Database Database Connection
|
|
// ================================================
|
|
|
|
public function connect() {
|
|
|
|
if ($this->settings->Environment == 'Production') {
|
|
$host = 'localhost';
|
|
} else {
|
|
$host = '192.168.1.190';
|
|
}
|
|
|
|
try {
|
|
|
|
$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 All Record Import Job");
|
|
|
|
$this->runImportJob();
|
|
|
|
$this->purgeImportJobs();
|
|
|
|
$this->log("Completed All Record Import Job");
|
|
}
|
|
|
|
// ==============
|
|
// Run Import Job
|
|
// ==============
|
|
|
|
private function runImportJob() {
|
|
|
|
$this->log("Processing Import Job");
|
|
|
|
$nextjob = $this->claimNextJob();
|
|
|
|
if (!$nextjob) {
|
|
$this->log("Nothing to do.");
|
|
return;
|
|
}
|
|
|
|
$importjob_serial = (int) $nextjob['importjob_serial'];
|
|
$importjob_file_path = (string) $nextjob['importjob_file_path'];
|
|
|
|
try {
|
|
|
|
$success = $this->parseCSVFile($importjob_file_path);
|
|
|
|
$SQL = "UPDATE importjobs
|
|
SET importjob_status = 'complete',
|
|
importjob_completed = NOW()
|
|
WHERE importjob_serial = :importjob_serial ";
|
|
|
|
$update = $this->connect()->prepare($SQL);
|
|
|
|
$update->execute([':importjob_serial' => $importjob_serial]);
|
|
} catch (Throwable $e) {
|
|
|
|
$SQL = "UPDATE importjobs
|
|
SET importjob_status = 'failed',
|
|
importjob_completed = NOW(),
|
|
importjob_error_message = :importjob_error_message
|
|
WHERE importjob_serial = :importjob_serial";
|
|
|
|
$fail = $this->connect()->prepare($SQL);
|
|
|
|
$fail->execute([':importjob_serial' => $importjob_serial, ':importjob_error_message' => $e->getMessage()]);
|
|
|
|
// Notify Admins of error
|
|
$this->sendWebHookAlert("Import Job {$importjob_serial} has failed. Check Error Logs for details.");
|
|
$this->email_admins("Import Job Failure.", "Import Job {$importjob_serial} has failed. Check Error Logs for details.");
|
|
}
|
|
}
|
|
|
|
// =================================
|
|
// Purge Imports Older then 24 Hours
|
|
// =================================
|
|
|
|
private function purgeImportJobs() {
|
|
|
|
$this->log("Purging Import Jobs Database Files");
|
|
|
|
$this->purgeFiles();
|
|
}
|
|
|
|
// =======================
|
|
// Claim Next Job in Queue
|
|
// =======================
|
|
|
|
private function claimNextJob() {
|
|
|
|
$this->connection->beginTransaction();
|
|
|
|
$SQL = "SELECT view_importjobs.*
|
|
FROM view_importjobs
|
|
WHERE view_importjobs.importjob_status = 'queued'
|
|
ORDER BY view_importjobs.importjob_created ASC
|
|
LIMIT 1 FOR UPDATE";
|
|
|
|
$statement = $this->connection->prepare($SQL);
|
|
|
|
$statement->execute();
|
|
|
|
$job = $statement->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$job) {
|
|
$this->connection->commit();
|
|
return null;
|
|
}
|
|
|
|
$SQL = "UPDATE importjobs
|
|
SET importjob_status = 'running'
|
|
WHERE importjob_serial = :importjob_serial";
|
|
|
|
$statement = $this->connection->prepare($SQL);
|
|
|
|
$statement->execute([':importjob_serial' => (int) $job['importjob_serial']]);
|
|
|
|
$this->connection->commit();
|
|
|
|
return $job;
|
|
}
|
|
|
|
// ====================
|
|
// Purges Files on Disk
|
|
// ====================
|
|
|
|
private function purgeFiles() {
|
|
|
|
$SQL = "select *
|
|
from importjobs
|
|
where importjobs.importjob_created < (NOW() - INTERVAL 24 HOUR)";
|
|
|
|
$importfiles = $this->connect()->query($SQL)->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
if (count($importfiles) == 0) {
|
|
$this->log("Nothing To Purge");
|
|
return;
|
|
}
|
|
|
|
foreach ($importfiles as $files) {
|
|
|
|
$filename = $files['importjob_file_path'];
|
|
|
|
unlink($filename);
|
|
}
|
|
|
|
$baseDir = './imports';
|
|
|
|
foreach (new DirectoryIterator($baseDir) as $item) {
|
|
|
|
if ($item->isDot() || !$item->isDir()) {
|
|
continue;
|
|
}
|
|
|
|
$folderPath = $item->getPathname();
|
|
|
|
// Check if directory is empty
|
|
$files = scandir($folderPath);
|
|
|
|
if ($files !== false && count($files) === 2) {
|
|
rmdir($folderPath);
|
|
}
|
|
}
|
|
|
|
$this->log("Purging Import Jobs Database Records");
|
|
|
|
$SQL = "delete from importjobs where importjobs.importjob_created < (NOW() - INTERVAL 24 HOUR)";
|
|
|
|
$this->connection->query($SQL);
|
|
}
|
|
|
|
// =======================
|
|
// Format Raw Phone Number
|
|
// =======================
|
|
|
|
public function formatPhoneNumber(string $phonenumber): string {
|
|
|
|
// Remove everything except digits
|
|
$digits = preg_replace('/\D+/', '', $phonenumber);
|
|
|
|
// Handle leading country code (US)
|
|
if (strlen($digits) === 11 && str_starts_with($digits, '1')) {
|
|
$digits = substr($digits, 1);
|
|
}
|
|
|
|
return substr($digits, 0, 3) . '-' . substr($digits, 3, 3) . '-' . substr($digits, 6, 4);
|
|
}
|
|
|
|
// ==============
|
|
// Parse CSV File
|
|
// ==============
|
|
|
|
public function parseCSVFile($file) {
|
|
|
|
$csvfile = fopen($file, 'r');
|
|
|
|
$RecordCount = 0;
|
|
|
|
while (($data = fgetcsv($csvfile, 0, ",", '"', '\\')) !== FALSE) {
|
|
|
|
$PermitFactsId = $data[1];
|
|
$EntryDate = $this->fixDate($data[2]);
|
|
$PermitNumber = $data[4];
|
|
$PermitDate = $this->fixDate($data[5]);
|
|
$ProjectAddress = $data[7];
|
|
$SubdivisionName = $data[8];
|
|
$ProjectType = $data[9];
|
|
$Company = substr($data[10], 0, 30);
|
|
$Address = substr($data[11], 0, 30);
|
|
$City = $data[12];
|
|
$State = $data[13];
|
|
$Zip = $data[14];
|
|
$Phone = $data[15];
|
|
$ProjectCity = $data[16];
|
|
$ProjectState = $data[17];
|
|
$Size = $data[18] ?: 0;
|
|
$Units = $data[19];
|
|
$BuildingType = $data[20];
|
|
$Value = $data[21] ?: 0;
|
|
$WorkType = $data[22];
|
|
$County = $data[23] ?: 0;
|
|
$Lot = $data[24];
|
|
$DistrictLL = $data[25];
|
|
$Ctl = $data[26];
|
|
$OwnerName = substr($data[27], 0, 30);
|
|
$OwnerAddress = substr($data[28], 0, 30);
|
|
$OwnerCity = $data[29];
|
|
$OwnerState = substr($data[30], 0, 2);
|
|
$OwnerZip = $data[31];
|
|
$OwnerPhone = $data[32];
|
|
$YTDPermits = $data[33] ?: 0;
|
|
$YTDValue = $data[34] ?: 0;
|
|
$TwelveMonthPreviousPermits = $data[35] ?: 0;
|
|
|
|
$County = $this->getCountiesByName($County)['county_serial'] ?? 0;
|
|
|
|
$SQL = "INSERT INTO permits (
|
|
permit_permit_facts_id,
|
|
permit_entry_date,
|
|
permit_permit_number,
|
|
permit_permit_date,
|
|
permit_project_addr,
|
|
permit_sub_div_name,
|
|
permit_project_type,
|
|
permit_company,
|
|
permit_address,
|
|
permit_city,
|
|
permit_state,
|
|
permit_zip,
|
|
permit_phone,
|
|
permit_project_city,
|
|
permit_project_state,
|
|
permit_size,
|
|
permit_units,
|
|
permit_building_type,
|
|
permit_value,
|
|
permit_work_type,
|
|
permit_county,
|
|
permit_lot,
|
|
permit_dist_ll,
|
|
permit_ct1,
|
|
permit_owner_name,
|
|
permit_owner_address,
|
|
permit_owner_city,
|
|
permit_owner_state,
|
|
permit_owner_zip,
|
|
permit_owner_phone,
|
|
permit_ytd_permits,
|
|
permit_ytd_value,
|
|
permit_12mth_prev_permits )
|
|
|
|
VALUES (
|
|
:permit_permit_facts_id,
|
|
:permit_entry_date,
|
|
:permit_permit_number,
|
|
:permit_permit_date,
|
|
:permit_project_addr,
|
|
:permit_sub_div_name,
|
|
:permit_project_type,
|
|
:permit_company,
|
|
:permit_address,
|
|
:permit_city,
|
|
:permit_state,
|
|
:permit_zip,
|
|
:permit_phone,
|
|
:permit_project_city,
|
|
:permit_project_state,
|
|
:permit_size,
|
|
:permit_units,
|
|
:permit_building_type,
|
|
:permit_value,
|
|
:permit_work_type,
|
|
:permit_county,
|
|
:permit_lot,
|
|
:permit_dist_ll,
|
|
:permit_ct1,
|
|
:permit_owner_name,
|
|
:permit_owner_address,
|
|
:permit_owner_city,
|
|
:permit_owner_state,
|
|
:permit_owner_zip,
|
|
:permit_owner_phone,
|
|
:permit_ytd_permits,
|
|
:permit_ytd_value,
|
|
:permit_12mth_prev_permits )";
|
|
|
|
$statement = $this->connection->prepare($SQL);
|
|
|
|
$statement->bindValue(":permit_permit_facts_id", $PermitFactsId);
|
|
$statement->bindValue(":permit_permit_number", $PermitNumber);
|
|
$statement->bindValue(":permit_entry_date", $EntryDate);
|
|
$statement->bindValue(":permit_permit_date", $PermitDate);
|
|
$statement->bindValue(":permit_project_addr", $ProjectAddress);
|
|
$statement->bindValue(":permit_sub_div_name", $SubdivisionName);
|
|
$statement->bindValue(":permit_project_type", $ProjectType);
|
|
$statement->bindValue(":permit_company", $Company);
|
|
$statement->bindValue(":permit_address", $Address);
|
|
$statement->bindValue(":permit_city", $City);
|
|
$statement->bindValue(":permit_state", $State);
|
|
$statement->bindValue(":permit_zip", $Zip);
|
|
$statement->bindValue(":permit_phone", $Phone);
|
|
$statement->bindValue(":permit_project_city", $ProjectCity);
|
|
$statement->bindValue(":permit_project_state", $ProjectState);
|
|
$statement->bindValue(":permit_size", $Size);
|
|
$statement->bindValue(":permit_units", $Units);
|
|
$statement->bindValue(":permit_building_type", $BuildingType);
|
|
$statement->bindValue(":permit_value", $Value);
|
|
$statement->bindValue(":permit_work_type", $WorkType);
|
|
$statement->bindValue(":permit_county", $County);
|
|
$statement->bindValue(":permit_lot", $Lot);
|
|
$statement->bindValue(":permit_dist_ll", $DistrictLL);
|
|
$statement->bindValue(":permit_ct1", $Ctl);
|
|
$statement->bindValue(":permit_owner_name", $OwnerName);
|
|
$statement->bindValue(":permit_owner_address", $OwnerAddress);
|
|
$statement->bindValue(":permit_owner_city", $OwnerCity);
|
|
$statement->bindValue(":permit_owner_state", $OwnerState);
|
|
$statement->bindValue(":permit_owner_zip", $OwnerZip);
|
|
$statement->bindValue(":permit_owner_phone", $OwnerPhone);
|
|
$statement->bindValue(":permit_ytd_permits", $YTDPermits);
|
|
$statement->bindValue(":permit_ytd_value", $YTDValue);
|
|
$statement->bindValue(":permit_12mth_prev_permits", $TwelveMonthPreviousPermits);
|
|
|
|
$statement->execute();
|
|
|
|
$RecordCount++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ========================
|
|
// Return a Counties Object
|
|
// ========================
|
|
|
|
public function getCountiesByName($county_name) {
|
|
|
|
$SQL = "select view_counties.*
|
|
from view_counties
|
|
where view_counties.county_name = '{$county_name}'";
|
|
|
|
$statement = $this->connection->prepare($SQL);
|
|
|
|
$statement->execute();
|
|
|
|
$county_name = $statement->fetch(PDO::FETCH_ASSOC);
|
|
|
|
return $county_name;
|
|
}
|
|
|
|
// ==========================
|
|
// Send an Alert/Notification
|
|
// ==========================
|
|
|
|
public function sendWebHookAlert($message, $title = "🚨 DEC Application Alert", $alerttype = "1fff00") {
|
|
|
|
$webhookUrl = (string) $this->settings->DiscordWebHookURL;
|
|
|
|
$data = [
|
|
'embeds' => [[
|
|
'title' => $title,
|
|
'description' => $message,
|
|
'color' => hexdec($alerttype),
|
|
'footer' => [
|
|
'text' => 'Real Estate Data Inc. System Notificiation',
|
|
],
|
|
'timestamp' => date('c'), // ISO 8601 format
|
|
]]
|
|
];
|
|
|
|
$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) {
|
|
$this->log('Curl error: ' . curl_error($ch));
|
|
}
|
|
|
|
curl_close($ch);
|
|
}
|
|
|
|
// ============
|
|
// Email Admins
|
|
// ============
|
|
|
|
public function email_admins($subject = "", $message = "") {
|
|
|
|
$this->log(date("Y-m-d H:i:s") . " - Emailing Admins...");
|
|
|
|
$settings = new SimpleXMLElement((file_exists("./xml/settings.xml") ? file_get_contents("./xml/settings.xml") : "<settings/>"));
|
|
|
|
// Server Variables
|
|
$host = (string) $settings->SmtpServer;
|
|
$username = (string) $settings->SmtpUserName;
|
|
$password = (string) $settings->SmtpPassword;
|
|
|
|
// Recipient Variables
|
|
$from_email = (string) $settings->FromEmail;
|
|
$from_name = (string) $settings->FromName;
|
|
|
|
$support_email = (string) $settings->SupportEmail;
|
|
$support_name = (string) $settings->SupportName;
|
|
|
|
if ((string) $settings->Environment == 'Production') {
|
|
|
|
$admin_email = (string) $settings->AdminEmail;
|
|
$admin_name = (string) $settings->AdminName;
|
|
}
|
|
|
|
// Content Variables
|
|
|
|
$attachment_name = "";
|
|
|
|
$mail = new PHPMailer(true);
|
|
|
|
try {
|
|
|
|
//Server settings
|
|
$mail->isSMTP();
|
|
$mail->Host = $host;
|
|
$mail->SMTPAuth = true;
|
|
$mail->Username = $username;
|
|
$mail->Password = $password;
|
|
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
|
|
$mail->Port = 465;
|
|
|
|
//Recipients
|
|
$mail->setFrom($from_email, $from_name);
|
|
$mail->addAddress($support_email, $support_name);
|
|
|
|
if ($settings->Environment == 'Production') {
|
|
$mail->addAddress($admin_email, $admin_name);
|
|
}
|
|
|
|
//Attachments
|
|
$mail->addAttachment($attachment_name);
|
|
|
|
//Content
|
|
$mail->isHTML(true);
|
|
$mail->Subject = $subject;
|
|
$mail->Body = $message;
|
|
|
|
$mail->send();
|
|
} catch (Exception $e) {
|
|
$this->log("Message could not be sent. Mailer Error: {$e->ErrorInfo}");
|
|
}
|
|
}
|
|
|
|
// ========================
|
|
// Return an Formatted Date
|
|
// ========================
|
|
|
|
public function fixDate($givenDate) {
|
|
|
|
$formattedDate = date('Y-m-d', strtotime($givenDate));
|
|
|
|
return $formattedDate;
|
|
}
|
|
}
|
|
|
|
?>
|