Files
trf_certest/public/userarea/routine_preview.php
T
2026-09-05 13:41:26 +02:00

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;