547 lines
17 KiB
PHP
547 lines
17 KiB
PHP
<?php // phpcs:ignoreFile
|
|
/**
|
|
* Mill Data Comparison - Compare categories across mills
|
|
*/
|
|
|
|
require __DIR__ . '/../session.php';
|
|
require __DIR__ . '/../userAccess.php';
|
|
require __DIR__ . '/../includes/millnames.php';
|
|
|
|
// ============================================================================
|
|
// Database Configuration
|
|
// ============================================================================
|
|
$config = [
|
|
'server' => '192.168.0.16',
|
|
'database' => 'lasucaai',
|
|
'username' => 'lasucaai',
|
|
'password' => 'is413#dfslw',
|
|
];
|
|
|
|
// ============================================================================
|
|
// Database Connection
|
|
// ============================================================================
|
|
function getCompareConnection($config) {
|
|
$connectionOptions = [
|
|
"Database" => $config['database'],
|
|
"Uid" => $config['username'],
|
|
"PWD" => $config['password'],
|
|
"TrustServerCertificate" => true,
|
|
"Encrypt" => false,
|
|
];
|
|
|
|
$conn = sqlsrv_connect($config['server'], $connectionOptions);
|
|
return $conn ?: null;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Data Queries
|
|
// ============================================================================
|
|
function getCompareSourceFiles($conn) {
|
|
$sql = "SELECT DISTINCT SourceFileName,
|
|
MIN(BeginningDate) as BeginningDate,
|
|
MAX(EndingDate) as EndingDate,
|
|
MAX(ProcessedAt) as ProcessedAt
|
|
FROM dbo.MillDataReports
|
|
GROUP BY SourceFileName
|
|
ORDER BY MAX(ProcessedAt) DESC";
|
|
|
|
$stmt = sqlsrv_query($conn, $sql);
|
|
$files = [];
|
|
|
|
if ($stmt) {
|
|
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
|
$files[] = $row;
|
|
}
|
|
}
|
|
|
|
return $files;
|
|
}
|
|
|
|
function getCompareCategories($conn, $sourceFileName = null) {
|
|
// Get all categories - simpler query that matches working pattern
|
|
$sql = "SELECT DISTINCT Category FROM dbo.MillDataMetrics WHERE Category IS NOT NULL ORDER BY Category";
|
|
$stmt = sqlsrv_query($conn, $sql);
|
|
$categories = [];
|
|
|
|
if ($stmt) {
|
|
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
|
$categories[] = $row['Category'];
|
|
}
|
|
}
|
|
|
|
return $categories;
|
|
}
|
|
|
|
function getMillsForCompare($conn, $sourceFileName) {
|
|
$sql = "SELECT ReportId, MillName
|
|
FROM dbo.MillDataReports
|
|
WHERE SourceFileName = ?
|
|
ORDER BY MillName";
|
|
|
|
$stmt = sqlsrv_query($conn, $sql, [$sourceFileName]);
|
|
$mills = [];
|
|
|
|
if ($stmt) {
|
|
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
|
$mills[] = $row;
|
|
}
|
|
}
|
|
|
|
return $mills;
|
|
}
|
|
|
|
function getComparisonData($conn, $sourceFileName, $category) {
|
|
// Get all metrics for this category across all mills in this file
|
|
$sql = "SELECT r.MillName, r.ReportId, m.ItemNumber, m.MetricName,
|
|
m.RunValue, m.RunValueNumeric, m.ToDateValue, m.ToDateValueNumeric
|
|
FROM dbo.MillDataMetrics m
|
|
JOIN dbo.MillDataReports r ON m.ReportId = r.ReportId
|
|
WHERE r.SourceFileName = ? AND m.Category = ?
|
|
ORDER BY m.ItemNumber, m.MetricName, r.MillName";
|
|
|
|
$stmt = sqlsrv_query($conn, $sql, [$sourceFileName, $category]);
|
|
$data = [];
|
|
|
|
if ($stmt) {
|
|
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
|
$data[] = $row;
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
function formatCompareDate($date) {
|
|
if ($date instanceof DateTime) {
|
|
return $date->format('m/d/Y');
|
|
}
|
|
return $date ?? '-';
|
|
}
|
|
|
|
// ============================================================================
|
|
// Main Logic
|
|
// ============================================================================
|
|
$conn = getCompareConnection($config);
|
|
$connectionError = $conn === null;
|
|
|
|
$sourceFiles = [];
|
|
$categories = [];
|
|
$mills = [];
|
|
$comparisonData = [];
|
|
$pivotedData = [];
|
|
$millNameLookup = [];
|
|
|
|
$selectedFile = isset($_GET['file']) ? trim($_GET['file']) : '';
|
|
$selectedCategory = isset($_GET['category']) ? trim($_GET['category']) : '';
|
|
|
|
if (!$connectionError) {
|
|
// Load mill name mappings
|
|
$millNameLookup = getMillNames($conn);
|
|
|
|
$sourceFiles = getCompareSourceFiles($conn);
|
|
$categories = getCompareCategories($conn);
|
|
|
|
// Default to first file
|
|
if (empty($selectedFile) && !empty($sourceFiles)) {
|
|
$selectedFile = $sourceFiles[0]['SourceFileName'];
|
|
}
|
|
|
|
if (!empty($selectedFile)) {
|
|
$mills = getMillsForCompare($conn, $selectedFile);
|
|
|
|
// Default to first category
|
|
if (empty($selectedCategory) && !empty($categories)) {
|
|
$selectedCategory = $categories[0];
|
|
}
|
|
|
|
if (!empty($selectedCategory)) {
|
|
$comparisonData = getComparisonData($conn, $selectedFile, $selectedCategory);
|
|
|
|
// Pivot data: group by metric, columns are mills
|
|
foreach ($comparisonData as $row) {
|
|
$metricKey = $row['ItemNumber'] . '|' . $row['MetricName'];
|
|
if (!isset($pivotedData[$metricKey])) {
|
|
$pivotedData[$metricKey] = [
|
|
'ItemNumber' => $row['ItemNumber'],
|
|
'MetricName' => $row['MetricName'],
|
|
'mills' => []
|
|
];
|
|
}
|
|
$pivotedData[$metricKey]['mills'][$row['MillName']] = [
|
|
'RunValue' => $row['RunValue'],
|
|
'RunValueNumeric' => $row['RunValueNumeric'],
|
|
'ToDateValue' => $row['ToDateValue'],
|
|
'ToDateValueNumeric' => $row['ToDateValueNumeric']
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
sqlsrv_close($conn);
|
|
}
|
|
|
|
// Get unique mill codes for column headers (raw codes used as data keys)
|
|
$millCodes = [];
|
|
foreach ($mills as $m) {
|
|
$millCodes[] = $m['MillName'];
|
|
}
|
|
|
|
// Build display names map for headers
|
|
$millDisplayNames = [];
|
|
foreach ($millCodes as $code) {
|
|
$millDisplayNames[$code] = getMillDisplayName($millNameLookup, $code);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Page Layout
|
|
// ============================================================================
|
|
$pageTitle = 'Mill Comparison';
|
|
$pageSubtitle = 'Compare metrics across mills';
|
|
$pageDescription = 'Side-by-side comparison of mill production data.';
|
|
$layoutWithoutSidebar = true;
|
|
$layoutReturnUrl = 'milldata.php';
|
|
$layoutReturnLabel = 'Back to Mill Data';
|
|
$assetBasePath = '../';
|
|
|
|
require __DIR__ . '/../includes/layout/header.php';
|
|
?>
|
|
|
|
<style>
|
|
.compare-controls {
|
|
display: flex;
|
|
gap: 1rem;
|
|
margin-bottom: 1.5rem;
|
|
flex-wrap: wrap;
|
|
align-items: flex-end;
|
|
}
|
|
|
|
.compare-control {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.3rem;
|
|
}
|
|
|
|
.compare-control label {
|
|
font-size: 0.8rem;
|
|
color: var(--text-muted);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
.compare-control select {
|
|
padding: 0.6rem 1rem;
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
font-size: 0.9rem;
|
|
background: var(--surface);
|
|
color: var(--text);
|
|
min-width: 200px;
|
|
}
|
|
|
|
.compare-table-container {
|
|
background: var(--surface);
|
|
border-radius: 8px;
|
|
border: 1px solid var(--border);
|
|
overflow-x: auto;
|
|
max-height: 600px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.compare-table {
|
|
width: 100%;
|
|
border-collapse: separate;
|
|
border-spacing: 0;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.compare-table th,
|
|
.compare-table td {
|
|
padding: 0.5rem 0.6rem;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
.compare-table th {
|
|
background: var(--surface-alt);
|
|
text-align: left;
|
|
font-weight: 600;
|
|
color: var(--text-muted);
|
|
border-bottom: 2px solid var(--border);
|
|
position: sticky;
|
|
top: 0;
|
|
white-space: nowrap;
|
|
z-index: 20;
|
|
}
|
|
|
|
/* Sticky first two columns */
|
|
.compare-table th.sticky-col,
|
|
.compare-table td.sticky-col {
|
|
position: sticky;
|
|
background: var(--surface);
|
|
z-index: 10;
|
|
}
|
|
|
|
.compare-table th.sticky-col {
|
|
background: var(--surface-alt);
|
|
z-index: 30;
|
|
}
|
|
|
|
.compare-table .sticky-col-1 {
|
|
left: 0;
|
|
min-width: 60px;
|
|
max-width: 60px;
|
|
}
|
|
|
|
.compare-table .sticky-col-2 {
|
|
left: 60px;
|
|
min-width: 200px;
|
|
max-width: 250px;
|
|
border-right: 2px solid var(--border);
|
|
}
|
|
|
|
.compare-table th.mill-header {
|
|
text-align: center;
|
|
min-width: 120px;
|
|
}
|
|
|
|
.compare-table th.mill-header .mill-name {
|
|
font-weight: 600;
|
|
color: var(--accent);
|
|
}
|
|
|
|
/* Mill column shading - alternating backgrounds */
|
|
.compare-table .mill-col-0 { background-color: rgba(33, 150, 243, 0.08); }
|
|
.compare-table .mill-col-1 { background-color: rgba(76, 175, 80, 0.08); }
|
|
.compare-table .mill-col-2 { background-color: rgba(156, 39, 176, 0.08); }
|
|
.compare-table .mill-col-3 { background-color: rgba(255, 152, 0, 0.08); }
|
|
.compare-table .mill-col-4 { background-color: rgba(0, 188, 212, 0.08); }
|
|
.compare-table .mill-col-5 { background-color: rgba(233, 30, 99, 0.08); }
|
|
.compare-table .mill-col-6 { background-color: rgba(139, 195, 74, 0.08); }
|
|
.compare-table .mill-col-7 { background-color: rgba(121, 85, 72, 0.08); }
|
|
|
|
/* Header shading matches columns */
|
|
.compare-table th.mill-col-0 { background-color: rgba(33, 150, 243, 0.15); }
|
|
.compare-table th.mill-col-1 { background-color: rgba(76, 175, 80, 0.15); }
|
|
.compare-table th.mill-col-2 { background-color: rgba(156, 39, 176, 0.15); }
|
|
.compare-table th.mill-col-3 { background-color: rgba(255, 152, 0, 0.15); }
|
|
.compare-table th.mill-col-4 { background-color: rgba(0, 188, 212, 0.15); }
|
|
.compare-table th.mill-col-5 { background-color: rgba(233, 30, 99, 0.15); }
|
|
.compare-table th.mill-col-6 { background-color: rgba(139, 195, 74, 0.15); }
|
|
.compare-table th.mill-col-7 { background-color: rgba(121, 85, 72, 0.15); }
|
|
|
|
.compare-table th.sub-header {
|
|
font-size: 0.75rem;
|
|
font-weight: 500;
|
|
top: 35px;
|
|
}
|
|
|
|
.compare-table tr:hover td {
|
|
background: var(--hover);
|
|
}
|
|
|
|
.compare-table tr:hover td.sticky-col {
|
|
background: var(--hover);
|
|
}
|
|
|
|
.compare-table .item-num {
|
|
font-weight: 600;
|
|
color: var(--accent);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.compare-table .metric-name {
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.compare-table .value-cell {
|
|
text-align: right;
|
|
font-family: 'Consolas', 'Monaco', monospace;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.compare-table .value-cell.negative {
|
|
color: #ef5350;
|
|
}
|
|
|
|
.compare-table .value-cell.best {
|
|
background: rgba(76, 175, 80, 0.15);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.compare-table .value-cell.worst {
|
|
background: rgba(244, 67, 54, 0.1);
|
|
}
|
|
|
|
.no-data {
|
|
text-align: center;
|
|
padding: 3rem;
|
|
color: var(--text-muted);
|
|
background: var(--surface);
|
|
border-radius: 8px;
|
|
border: 1px solid var(--border);
|
|
}
|
|
|
|
.compare-summary {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
gap: 1rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.compare-stat {
|
|
background: var(--surface);
|
|
padding: 1rem;
|
|
border-radius: 8px;
|
|
text-align: center;
|
|
border: 1px solid var(--border);
|
|
}
|
|
|
|
.compare-stat .value {
|
|
font-size: 1.5rem;
|
|
font-weight: 600;
|
|
color: var(--accent);
|
|
}
|
|
|
|
.compare-stat .label {
|
|
font-size: 0.8rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.view-toggle {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.view-toggle button {
|
|
padding: 0.5rem 1rem;
|
|
border: 1px solid var(--border);
|
|
background: var(--surface);
|
|
color: var(--text);
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.view-toggle button.active {
|
|
background: var(--accent);
|
|
color: white;
|
|
border-color: var(--accent);
|
|
}
|
|
</style>
|
|
|
|
<div class="app-content">
|
|
<section class="data-panel">
|
|
<?php if ($connectionError): ?>
|
|
<div class="no-data">Unable to connect to the database.</div>
|
|
<?php else: ?>
|
|
|
|
<form class="compare-controls" method="get">
|
|
<div class="compare-control">
|
|
<label>Report File</label>
|
|
<select name="file" onchange="this.form.submit()">
|
|
<?php if (empty($sourceFiles)): ?>
|
|
<option value="">No reports available</option>
|
|
<?php else: ?>
|
|
<?php foreach ($sourceFiles as $file): ?>
|
|
<option value="<?= htmlspecialchars($file['SourceFileName']) ?>"
|
|
<?= $file['SourceFileName'] === $selectedFile ? 'selected' : '' ?>>
|
|
<?= htmlspecialchars($file['SourceFileName']) ?>
|
|
(<?= formatCompareDate($file['BeginningDate']) ?> - <?= formatCompareDate($file['EndingDate']) ?>)
|
|
</option>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="compare-control">
|
|
<label>Category</label>
|
|
<select name="category" onchange="this.form.submit()">
|
|
<?php if (empty($categories)): ?>
|
|
<option value="">No categories available</option>
|
|
<?php else: ?>
|
|
<?php foreach ($categories as $cat): ?>
|
|
<option value="<?= htmlspecialchars($cat) ?>"
|
|
<?= $cat === $selectedCategory ? 'selected' : '' ?>>
|
|
<?= htmlspecialchars($cat) ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</select>
|
|
</div>
|
|
</form>
|
|
|
|
<div class="compare-summary">
|
|
<div class="compare-stat">
|
|
<div class="value"><?= count($millCodes) ?></div>
|
|
<div class="label">Mills</div>
|
|
</div>
|
|
<div class="compare-stat">
|
|
<div class="value"><?= count($pivotedData) ?></div>
|
|
<div class="label">Metrics</div>
|
|
</div>
|
|
<div class="compare-stat">
|
|
<div class="value"><?= htmlspecialchars($selectedCategory ?: '-') ?></div>
|
|
<div class="label">Category</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if (!empty($pivotedData) && !empty($millCodes)): ?>
|
|
<div class="compare-table-container">
|
|
<table class="compare-table">
|
|
<thead>
|
|
<tr>
|
|
<th rowspan="2" class="sticky-col sticky-col-1">Item</th>
|
|
<th rowspan="2" class="sticky-col sticky-col-2">Metric</th>
|
|
<?php foreach ($millCodes as $idx => $millCode): ?>
|
|
<th colspan="2" class="mill-header mill-col-<?= $idx % 8 ?>">
|
|
<span class="mill-name"><?= htmlspecialchars($millDisplayNames[$millCode]) ?></span>
|
|
</th>
|
|
<?php endforeach; ?>
|
|
</tr>
|
|
<tr>
|
|
<?php foreach ($millCodes as $idx => $millCode): ?>
|
|
<th class="sub-header mill-col-<?= $idx % 8 ?>">RUN</th>
|
|
<th class="sub-header mill-col-<?= $idx % 8 ?>">TO DATE</th>
|
|
<?php endforeach; ?>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($pivotedData as $metric): ?>
|
|
<tr>
|
|
<td class="item-num sticky-col sticky-col-1"><?= htmlspecialchars($metric['ItemNumber'] ?? '') ?></td>
|
|
<td class="metric-name sticky-col sticky-col-2"><?= htmlspecialchars($metric['MetricName'] ?? '') ?></td>
|
|
<?php foreach ($millCodes as $idx => $millCode): ?>
|
|
<?php
|
|
$millData = $metric['mills'][$millCode] ?? null;
|
|
$runValue = $millData['RunValue'] ?? '-';
|
|
$toDateValue = $millData['ToDateValue'] ?? '-';
|
|
$runNumeric = $millData['RunValueNumeric'] ?? null;
|
|
$toDateNumeric = $millData['ToDateValueNumeric'] ?? null;
|
|
$colClass = 'mill-col-' . ($idx % 8);
|
|
?>
|
|
<td class="value-cell <?= $colClass ?> <?= ($runNumeric ?? 0) < 0 ? 'negative' : '' ?>">
|
|
<?= htmlspecialchars($runValue) ?>
|
|
</td>
|
|
<td class="value-cell <?= $colClass ?> <?= ($toDateNumeric ?? 0) < 0 ? 'negative' : '' ?>">
|
|
<?= htmlspecialchars($toDateValue) ?>
|
|
</td>
|
|
<?php endforeach; ?>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="no-data">
|
|
<p>No comparison data available. Select a file and category above.</p>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php endif; ?>
|
|
</section>
|
|
</div>
|
|
|
|
<?php require __DIR__ . '/../includes/layout/footer.php'; ?>
|