37253-vm/import.php
2026-01-06 10:04:24 +00:00

354 lines
14 KiB
PHP

<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
require_once 'db/config.php';
$messages = [];
$conn = db();
function create_lifecycle_table($conn) {
$messages = [];
try {
$sql_lifecycle = "
CREATE TABLE IF NOT EXISTS lifecycle (
id INT AUTO_INCREMENT PRIMARY KEY,
product_id VARCHAR(255),
custcode VARCHAR(255),
first_ship_month VARCHAR(255),
last_ship_month VARCHAR(255),
total_ship INT,
avg_ship FLOAT,
maxstores INT,
mock_pct FLOAT,
store_count INT,
runrate FLOAT
);";
$conn->exec($sql_lifecycle);
$messages['success'][] = "Table 'lifecycle' created or already exists.";
} catch (PDOException $e) {
$messages['danger'][] = "Table creation failed: " . $e->getMessage();
}
return $messages;
}
function create_customers_table($conn) {
$messages = [];
try {
$sql_customers = "
CREATE TABLE IF NOT EXISTS customers (
id INT AUTO_INCREMENT PRIMARY KEY,
custcode VARCHAR(255),
custname VARCHAR(255),
channel VARCHAR(255),
country VARCHAR(255),
maxstores INT
);";
$conn->exec($sql_customers);
$messages['success'][] = "Table 'customers' created or already exists.";
} catch (PDOException $e) {
$messages['danger'][] = "Table creation failed: " . $e->getMessage();
}
return $messages;
}
function create_products_table($conn) {
$messages = [];
try {
$sql_products = "
CREATE TABLE IF NOT EXISTS products (
product_id VARCHAR(255) PRIMARY KEY,
prodname VARCHAR(255),
brand VARCHAR(255),
subbrand VARCHAR(255),
sub_brand_abbreviation VARCHAR(255),
listprice FLOAT
);";
$conn->exec($sql_products);
$messages['success'][] = "Table 'products' created or already exists.";
} catch (PDOException $e) {
$messages['danger'][] = "Table creation failed: " . $e->getMessage();
}
return $messages;
}
function create_shipments_table($conn) {
$messages = [];
try {
$sql_shipments = "
CREATE TABLE IF NOT EXISTS shipments (
id INT AUTO_INCREMENT PRIMARY KEY,
product_id VARCHAR(255),
month VARCHAR(255),
custcode VARCHAR(255),
ship INT
);";
$conn->exec($sql_shipments);
$messages['success'][] = "Table 'shipments' created or already exists.";
} catch (PDOException $e) {
$messages['danger'][] = "Table creation failed: " . $e->getMessage();
}
return $messages;
}
function import_csv($conn, $filePath, $tableName, $fieldMap, &$messages) {
try {
$conn->exec("TRUNCATE TABLE `$tableName`");
} catch (PDOException $e) {
$messages['danger'][] = "Error truncating table $tableName: " . $e->getMessage();
return ['count' => 0, 'error' => "Error truncating table: " . $e->getMessage()];
}
if (!file_exists($filePath) || !is_readable($filePath)) {
return ['count' => 0, 'error' => "File not found or not readable: $filePath"];
}
$file = fopen($filePath, 'r');
if (!$file) {
return ['count' => 0, 'error' => "Failed to open file: $filePath"];
}
$header = fgetcsv($file);
if (!$header) {
return ['count' => 0, 'error' => "Failed to read header from file: $filePath"];
}
$indices = [];
foreach ($fieldMap as $dbField => $csvField) {
$index = array_search($csvField, $header);
if ($index === false) {
// Allow for flexibility with headers that have dots
$csvFieldWithDot = str_replace('_', '.', $csvField);
if (in_array($csvFieldWithDot, $header)) {
$index = array_search($csvFieldWithDot, $header);
} else {
return ['count' => 0, 'error' => "Column '$csvField' (or '$csvFieldWithDot') not found in $tableName CSV."];
}
}
$indices[$dbField] = $index;
}
$placeholders = implode(', ', array_fill(0, count($fieldMap), '?'));
$columns = implode(', ', array_keys($fieldMap));
if ($tableName === 'products') {
$sql = "INSERT IGNORE INTO $tableName ($columns) VALUES ($placeholders)";
} else {
$sql = "INSERT INTO $tableName ($columns) VALUES ($placeholders)";
}
$stmt = $conn->prepare($sql);
$count = 0;
$conn->beginTransaction();
while (($row = fgetcsv($file)) !== false) {
$params = [];
foreach ($fieldMap as $dbField => $csvField) {
$value = $row[$indices[$dbField]];
if ($tableName === 'lifecycle') {
if (in_array($dbField, ['total_ship', 'maxstores', 'store_count'])) {
$params[] = (int)filter_var($value, FILTER_SANITIZE_NUMBER_INT);
} elseif (in_array($dbField, ['avg_ship', 'mock_pct', 'runrate'])) {
$params[] = (float)filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
} else {
$params[] = trim($value);
}
} elseif ($tableName === 'customers') {
if (in_array($dbField, ['maxstores'])) {
$params[] = (int)filter_var($value, FILTER_SANITIZE_NUMBER_INT);
} else {
$params[] = trim($value);
}
} elseif ($tableName === 'products') {
if (in_array($dbField, ['listprice'])) {
$params[] = (float)filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
} else {
$params[] = trim($value);
}
} elseif ($tableName === 'shipments') {
if (in_array($dbField, ['ship'])) {
$params[] = (int)filter_var($value, FILTER_SANITIZE_NUMBER_INT);
} else {
$params[] = trim($value);
}
} else {
$params[] = trim($value);
}
}
$stmt->execute($params);
$count++;
}
$conn->commit();
fclose($file);
return ['count' => $count, 'error' => null];
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$messages = array_merge_recursive($messages, create_lifecycle_table($conn));
$messages = array_merge_recursive($messages, create_customers_table($conn));
$messages = array_merge_recursive($messages, create_products_table($conn));
$messages = array_merge_recursive($messages, create_shipments_table($conn));
$uploadDir = 'uploads/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
$files = [
'lifecycle' => ['lifecycle', [
'product_id' => 'product_id',
'custcode' => 'custcode',
'first_ship_month' => 'first_ship_month',
'last_ship_month' => 'last_ship_month',
'total_ship' => 'total_ship',
'avg_ship' => 'avg_ship',
'maxstores' => 'maxstores',
'mock_pct' => 'mock_pct',
'store_count' => 'store_count',
'runrate' => 'runrate'
]],
'customers' => ['customers', [
'custcode' => 'custcode',
'custname' => 'custname',
'channel' => 'channel',
'country' => 'country',
'maxstores' => 'maxstores'
]],
'products' => ['products', [
'product_id' => 'product_id',
'prodname' => 'prodname',
'brand' => 'brand',
'subbrand' => 'subbrand',
'sub_brand_abbreviation' => 'sub_brand.abbreviation',
'listprice' => 'listprice'
]],
'shipments' => ['shipments', [
'product_id' => 'product_id',
'month' => 'month',
'custcode' => 'custcode',
'ship' => 'ship'
]]
];
foreach ($files as $fileKey => $fileInfo) {
if (isset($_FILES[$fileKey]) && $_FILES[$fileKey]['error'] == UPLOAD_ERR_OK) {
$tableName = $fileInfo[0];
$fieldMap = $fileInfo[1];
$tmpName = $_FILES[$fileKey]['tmp_name'];
$filePath = $uploadDir . basename($_FILES[$fileKey]['name']);
if (move_uploaded_file($tmpName, $filePath)) {
$result = import_csv($conn, $filePath, $tableName, $fieldMap, $messages);
if ($result['error']) {
$messages['danger'][] = "Error importing $tableName: " . $result['error'];
} else {
$messages['success'][] = "Successfully imported " . $result['count'] . " records into '$tableName'.";
}
} else {
$messages['danger'][] = "Failed to move uploaded file for $fileKey.";
}
} elseif (isset($_FILES[$fileKey]) && !empty($_FILES[$fileKey]['name'])) {
$errorCode = $_FILES[$fileKey]['error'];
$errorMessage = "An unknown error occurred.";
if ($errorCode === UPLOAD_ERR_INI_SIZE) {
$maxSize = ini_get('upload_max_filesize');
$errorMessage = "The file is too large. The server's maximum upload size is {$maxSize}.";
}
$messages['warning'][] = "An error occurred with the '$fileKey' upload: " . $errorMessage;
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data Importer</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">CRUD App</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/import.php">Import Data</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-5">
<div class="row">
<div class="col-md-8 offset-md-2">
<div class="card">
<div class="card-header">
<h1 class="card-title text-center">Data Importer</h1>
</div>
<div class="card-body">
<p class="card-text">Upload your CSV files to populate the database. The system will create the necessary tables and import the data.</p>
<?php if (!empty($messages)): ?>
<div class="mb-4">
<?php foreach ($messages as $type => $msgs): ?>
<?php foreach ($msgs as $msg): ?>
<div class="alert alert-<?php echo htmlspecialchars($type); ?>" role="alert">
<?php echo htmlspecialchars($msg); ?>
</div>
<?php endforeach; ?>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form action="import.php" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label for="lifecycle" class="form-label">Lifecycle CSV File</label>
<input class="form-control" type="file" id="lifecycle" name="lifecycle">
<div class="form-text">Fields: product_id, custcode, first_ship_month, last_ship_month, total_ship, avg_ship, maxstores, mock_pct, store_count, runrate</div>
</div>
<div class="mb-3">
<label for="customers" class="form-label">Customers CSV File</label>
<input class="form-control" type="file" id="customers" name="customers">
<div class="form-text">Fields: custcode, custname, channel, country, maxstores</div>
</div>
<div class="mb-3">
<label for="products" class="form-label">Products CSV File</label>
<input class="form-control" type="file" id="products" name="products">
<div class="form-text">Fields: product_id, prodname, brand, subbrand, sub_brand.abbreviation, listprice</div>
</div>
<div class="mb-3">
<label for="shipments" class="form-label">Shipments CSV File</label>
<input class="form-control" type="file" id="shipments" name="shipments">
<div class="form-text">Fields: product_id, month, custcode, ship</div>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg"><i class="bi bi-cloud-upload-fill"></i> Preview & Import</button>
</div>
</form>
</div>
<div class="card-footer text-muted">
Ensure your CSV files have a header row that matches the specified fields.
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>