| Server IP : 103.243.232.44 / Your IP : 216.73.216.237 Web Server : LiteSpeed System : Linux server17213-10344.hostycare.online 5.14.0-687.38.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Aug 12 17:19:12 EDT 2026 x86_64 User : iamakash ( 1400) PHP Version : 8.1.34 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /home/iamakash/.trash/lib/ |
Upload File : |
<?php
namespace Shaurya;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PDO;
use RuntimeException;
/**
* Parses a WPI monthly-index Excel file (as downloaded from eaindustry.nic.in)
* and upserts commodities + monthly index values into the database.
*
* Expected layout (single sheet, usually named MONTHLY_INDEX):
* Row 0: COMM_NAME | COMM_CODE | COMM_WT | INDX042012 | INDX052012 | ... | INDX022026
* Row 1+: one row per commodity
*
* Commodity hierarchy is inferred from the 10-digit COMM_CODE:
* - positions 0..1 = Level 2 (section) e.g. 1100000000 = "I PRIMARY ARTICLES"
* - positions 0..3 = Level 3 e.g. 1101000000 = "(A). FOOD ARTICLES"
* - positions 0..5 = Level 4 e.g. 1101010000 = "a. FOOD GRAINS"
* - positions 0..7 = Level 5 e.g. 1101010100 = "a1. CEREALS"
* - positions 0..9 = Level 6 (leaf) e.g. 1101010101 = "Paddy"
* - "1000000000" = "All commodities" is Level 2 (root, parent NULL)
*/
class ExcelImporter
{
private PDO $pdo;
private string $baseYear;
public function __construct(PDO $pdo, string $baseYear = '2011-12')
{
$this->pdo = $pdo;
$this->baseYear = $baseYear;
}
/**
* Returns an associative summary:
* sheet, commodities_seen, periods_seen, values_written, latest_period, periods: [[y,m], ...]
*/
public function import(string $filePath, ?int $uploadId = null): array
{
if (!is_file($filePath)) {
throw new RuntimeException("File not found: $filePath");
}
$spreadsheet = IOFactory::load($filePath);
$sheet = $this->locateDataSheet($spreadsheet);
$highestRow = $sheet->getHighestDataRow();
$highestCol = $sheet->getHighestDataColumn();
$highestColIdx = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestCol);
// --- Read header row (row 1 in 1-indexed PhpSpreadsheet coords) ---
$headers = [];
for ($c = 1; $c <= $highestColIdx; $c++) {
$headers[$c] = trim((string)$sheet->getCell([$c, 1])->getValue());
}
$colName = $this->findHeaderColumn($headers, 'COMM_NAME');
$colCode = $this->findHeaderColumn($headers, 'COMM_CODE');
$colWt = $this->findHeaderColumn($headers, 'COMM_WT');
if (!$colName || !$colCode) {
throw new RuntimeException(
'Could not find COMM_NAME / COMM_CODE columns in header row. '
. 'Header seen: ' . implode(', ', array_filter($headers))
);
}
// Map INDX columns to (year, month)
// Header like "INDX042012" → month=04, year=2012
$indxCols = []; // colIdx => ['y'=>int,'m'=>int,'key'=>'202204']
foreach ($headers as $idx => $h) {
if (preg_match('/^INDX(\d{2})(\d{4})$/', $h, $m)) {
$mon = (int)$m[1];
$yr = (int)$m[2];
if ($mon >= 1 && $mon <= 12 && $yr >= 1900) {
$indxCols[$idx] = [
'y' => $yr,
'm' => $mon,
'sortKey' => $yr * 100 + $mon,
];
}
}
}
if (!$indxCols) {
throw new RuntimeException('No INDXmmyyyy columns found in header row.');
}
// Identify the latest 2 periods (provisional per SOW)
$allPeriods = array_values($indxCols);
usort($allPeriods, fn($a, $b) => $b['sortKey'] <=> $a['sortKey']);
$provisionalSet = [];
foreach (array_slice($allPeriods, 0, 2) as $p) {
$provisionalSet[$p['sortKey']] = true;
}
$latestPeriod = $allPeriods ? sprintf('%02d%04d', $allPeriods[0]['m'], $allPeriods[0]['y']) : null;
// --- Prepared statements ---
$commUpsert = $this->pdo->prepare(
'INSERT INTO commodities
(comm_code, comm_name, level, parent_code, comm_wt, base_year, display_name)
VALUES (:code, :name, :level, :parent, :wt, :base, :display)
ON DUPLICATE KEY UPDATE
comm_name = VALUES(comm_name),
level = VALUES(level),
parent_code = VALUES(parent_code),
comm_wt = VALUES(comm_wt),
display_name = VALUES(display_name)'
);
$commoditiesSeen = 0;
$valuesWritten = 0;
$rowBuffer = []; // for batched monthly_indices INSERT
$batchSize = 500;
$this->pdo->beginTransaction();
try {
for ($r = 2; $r <= $highestRow; $r++) {
$name = trim((string)$sheet->getCell([$colName, $r])->getValue());
$codeRaw = $sheet->getCell([$colCode, $r])->getValue();
if ($codeRaw === null || $codeRaw === '') continue;
if (is_float($codeRaw)) {
$code = rtrim(rtrim(sprintf('%.0f', $codeRaw), '0'), '.');
$code = sprintf('%.0f', $codeRaw);
} else {
$code = trim((string)$codeRaw);
}
// Normalize to 10 digits left-padded
$code = str_pad($code, 10, '0', STR_PAD_LEFT);
if (!preg_match('/^\d{10}$/', $code) || $name === '') {
continue;
}
$wt = $colWt ? $sheet->getCell([$colWt, $r])->getValue() : null;
$wt = is_numeric($wt) ? (float)$wt : null;
$level = self::levelOf($code);
$parent = self::parentOf($code, $level);
$display = $code . ' - ' . $name;
$commUpsert->execute([
':code' => $code,
':name' => $name,
':level' => $level,
':parent' => $parent,
':wt' => $wt,
':base' => $this->baseYear,
':display' => $display,
]);
$commoditiesSeen++;
// Read INDX values for this commodity
foreach ($indxCols as $idx => $p) {
$val = $sheet->getCell([$idx, $r])->getValue();
if ($val === null || $val === '') {
$val = null;
} elseif (!is_numeric($val)) {
continue; // skip non-numeric garbage
} else {
$val = (float)$val;
}
$isProv = isset($provisionalSet[$p['sortKey']]) ? 1 : 0;
$rowBuffer[] = [$code, $this->baseYear, $p['y'], $p['m'], $val, $isProv, $uploadId];
if (count($rowBuffer) >= $batchSize) {
$valuesWritten += $this->flushValues($rowBuffer);
$rowBuffer = [];
}
}
}
if ($rowBuffer) {
$valuesWritten += $this->flushValues($rowBuffer);
}
$this->pdo->commit();
} catch (\Throwable $e) {
$this->pdo->rollBack();
throw $e;
}
return [
'sheet' => $sheet->getTitle(),
'commodities_seen' => $commoditiesSeen,
'periods_seen' => count($indxCols),
'values_written' => $valuesWritten,
'latest_period' => $latestPeriod,
];
}
// ---------- helpers ----------
private function locateDataSheet(Spreadsheet $wb): Worksheet
{
// Prefer a sheet literally named MONTHLY_INDEX; else any sheet whose
// first row starts with COMM_NAME, COMM_CODE.
foreach ($wb->getAllSheets() as $sh) {
if (strcasecmp($sh->getTitle(), 'MONTHLY_INDEX') === 0) {
return $sh;
}
}
foreach ($wb->getAllSheets() as $sh) {
$h1 = trim((string)$sh->getCell('A1')->getValue());
$h2 = trim((string)$sh->getCell('B1')->getValue());
if (strcasecmp($h1, 'COMM_NAME') === 0 && strcasecmp($h2, 'COMM_CODE') === 0) {
return $sh;
}
}
// Fall back to first sheet
return $wb->getSheet(0);
}
private function findHeaderColumn(array $headers, string $needle): ?int
{
foreach ($headers as $idx => $h) {
if (strcasecmp(trim($h), $needle) === 0) {
return $idx;
}
}
return null;
}
private function flushValues(array $rows): int
{
if (!$rows) return 0;
$placeholders = rtrim(str_repeat('(?,?,?,?,?,?,?),', count($rows)), ',');
$sql = "INSERT INTO monthly_indices
(comm_code, base_year, period_year, period_month, index_value, is_provisional, upload_id)
VALUES $placeholders
ON DUPLICATE KEY UPDATE
index_value = VALUES(index_value),
is_provisional = VALUES(is_provisional),
upload_id = VALUES(upload_id)";
$stmt = $this->pdo->prepare($sql);
$flat = [];
foreach ($rows as $r) {
foreach ($r as $v) { $flat[] = $v; }
}
$stmt->execute($flat);
return count($rows);
}
public static function levelOf(string $code): int
{
$code = str_pad($code, 10, '0', STR_PAD_LEFT);
$stripped = rtrim($code, '0');
$tz = 10 - strlen($stripped);
if ($tz >= 8) return 2; // "All commodities" (tz=9) or section (tz=8)
if ($tz >= 6) return 3;
if ($tz >= 4) return 4;
if ($tz >= 2) return 5;
return 6;
}
public static function parentOf(string $code, int $level): ?string
{
if ($level <= 2) return null;
$sig = 2 * ($level - 2); // significant digits of parent
return substr($code, 0, $sig) . str_repeat('0', 10 - $sig);
}
}