185 lines
5.9 KiB
PHP
185 lines
5.9 KiB
PHP
<?php
|
|
|
|
/**
|
|
* routine_preview.php
|
|
* -------------------
|
|
* Anteprima di una routine a motore su un XLS caricato temporaneamente.
|
|
*
|
|
* - Riceve via multipart/form-data: `file` (XLS/XLSX) e `idroutine`.
|
|
* - NON salva nulla: il file viene letto dal tmp di PHP ed elaborato in RAM.
|
|
* - Legge la PRIMA riga dati subito dopo l'header row (routine.headerrow).
|
|
* - Impacchetta la riga nel formato excelData atteso dal motore:
|
|
* [ ['excelrow' => N, 'data' => [0=>.., 1=>.., ...]] ]
|
|
* - Include _engine.php e applica applyRoutine().
|
|
* - Risponde JSON con la riga elaborata + intestazioni + lettere colonna.
|
|
*/
|
|
|
|
ob_start();
|
|
session_start();
|
|
require_once '../../vendor/autoload.php';
|
|
|
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
|
|
|
Dotenv\Dotenv::createImmutable(dirname(__DIR__, 2))->safeLoad();
|
|
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
$response = [
|
|
'error' => '',
|
|
'headers' => [], // intestazioni prese dalla header row
|
|
'letters' => [], // lettere colonna (A, B, C, ...) allineate ai valori
|
|
'row' => [], // valori della riga DOPO l'applicazione del motore
|
|
'excelrow' => 0, // numero riga excel elaborata
|
|
];
|
|
|
|
/**
|
|
* Indice zero-based -> lettera colonna Excel (0 -> A, 25 -> Z, 26 -> AA...).
|
|
*/
|
|
function previewIndexToCol($index)
|
|
{
|
|
$index = (int)$index;
|
|
if ($index < 0) {
|
|
return '';
|
|
}
|
|
$letters = '';
|
|
$index += 1;
|
|
while ($index > 0) {
|
|
$rem = ($index - 1) % 26;
|
|
$letters = chr(ord('A') + $rem) . $letters;
|
|
$index = intdiv($index - 1, 26);
|
|
}
|
|
return $letters;
|
|
}
|
|
|
|
try {
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
throw new Exception('Richiesta non valida.');
|
|
}
|
|
|
|
$idroutine = (int)($_POST['idroutine'] ?? 0);
|
|
if ($idroutine <= 0) {
|
|
throw new Exception('ID routine mancante.');
|
|
}
|
|
|
|
if (
|
|
empty($_FILES['file']) ||
|
|
!isset($_FILES['file']['tmp_name']) ||
|
|
$_FILES['file']['error'] !== UPLOAD_ERR_OK
|
|
) {
|
|
throw new Exception('Nessun file caricato o upload non riuscito.');
|
|
}
|
|
|
|
// --- Recupera la routine e verifica che sia a motore ---
|
|
require_once __DIR__ . '/class/db-functions.php';
|
|
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
|
|
|
$stmt = $pdo->prepare("SELECT idroutine, name, engine, headerrow FROM routine WHERE idroutine = :id LIMIT 1");
|
|
$stmt->execute([':id' => $idroutine]);
|
|
$routine = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$routine) {
|
|
throw new Exception('Routine non trovata.');
|
|
}
|
|
if ((int)$routine['engine'] !== 1) {
|
|
throw new Exception('Questa routine non è di tipo Motore: anteprima non disponibile.');
|
|
}
|
|
|
|
$headerRow = max(1, (int)$routine['headerrow']); // riga 1-based dell'intestazione
|
|
|
|
// --- Legge l'XLS dal file temporaneo (nessun salvataggio) ---
|
|
$tmpPath = $_FILES['file']['tmp_name'];
|
|
|
|
$reader = IOFactory::createReaderForFile($tmpPath);
|
|
$reader->setReadDataOnly(true);
|
|
$spreadsheet = $reader->load($tmpPath);
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
|
|
$highestColIndex = Coordinate::columnIndexFromString($sheet->getHighestDataColumn()); // 1-based
|
|
$highestRow = $sheet->getHighestDataRow();
|
|
|
|
// --- Intestazioni dalla header row ---
|
|
$headers = [];
|
|
$letters = [];
|
|
for ($c = 1; $c <= $highestColIndex; $c++) {
|
|
$val = $sheet->getCell([$c, $headerRow])->getValue();
|
|
$headers[] = ($val === null ? '' : (string)$val);
|
|
$letters[] = previewIndexToCol($c - 1); // zero-based -> lettera
|
|
}
|
|
|
|
// --- Trova la prima riga dati valorizzata dopo l'header ---
|
|
$dataRowNumber = null;
|
|
$rowValues = null;
|
|
|
|
for ($r = $headerRow + 1; $r <= $highestRow; $r++) {
|
|
$vals = [];
|
|
$hasValue = false;
|
|
for ($c = 1; $c <= $highestColIndex; $c++) {
|
|
$v = $sheet->getCell([$c, $r])->getValue();
|
|
$v = ($v === null ? '' : (string)$v);
|
|
if ($v !== '') {
|
|
$hasValue = true;
|
|
}
|
|
$vals[$c - 1] = $v; // indice zero-based, allineato alle colonne
|
|
}
|
|
if ($hasValue) {
|
|
$dataRowNumber = $r;
|
|
$rowValues = $vals;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($dataRowNumber === null) {
|
|
throw new Exception('Nessuna riga dati valorizzata trovata dopo la riga di intestazione (' . $headerRow . ').');
|
|
}
|
|
|
|
// --- Impacchetta nel formato excelData e applica il motore ---
|
|
$excelData = [
|
|
[
|
|
'excelrow' => $dataRowNumber,
|
|
'data' => $rowValues,
|
|
],
|
|
];
|
|
|
|
// Il motore legge gli step dal DB in base a idroutine.
|
|
$routineData = [
|
|
'idroutine' => $idroutine,
|
|
'name' => $routine['name'],
|
|
'headerrow' => $headerRow,
|
|
'xls_headers' => $headers,
|
|
];
|
|
|
|
require_once __DIR__ . '/routines/_engine.php';
|
|
applyRoutine($excelData, $routineData); // modifica in place
|
|
|
|
$processed = $excelData[0]['data'] ?? [];
|
|
|
|
// Riallinea l'output all'ampiezza delle intestazioni (il merge può creare indici nuovi).
|
|
$maxIndex = max(
|
|
count($headers) - 1,
|
|
empty($processed) ? 0 : max(array_keys($processed))
|
|
);
|
|
|
|
$outRow = [];
|
|
$outLetters = [];
|
|
$outHeaders = [];
|
|
for ($i = 0; $i <= $maxIndex; $i++) {
|
|
$outRow[] = array_key_exists($i, $processed) ? (string)$processed[$i] : '';
|
|
$outLetters[] = previewIndexToCol($i);
|
|
$outHeaders[] = array_key_exists($i, $headers) ? $headers[$i] : '';
|
|
}
|
|
|
|
$response['headers'] = $outHeaders;
|
|
$response['letters'] = $outLetters;
|
|
$response['row'] = $outRow;
|
|
$response['excelrow'] = $dataRowNumber;
|
|
} catch (Throwable $e) {
|
|
$response['error'] = 'Errore anteprima: ' . $e->getMessage();
|
|
error_log('routine_preview.php: ' . $e->getMessage());
|
|
}
|
|
|
|
ob_end_clean();
|
|
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
|
exit;
|