Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion modules/import/actions.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@
$fields = (array) post('fields');
$page = post('page');

$limit = 500;
// Batch piccolo: richieste brevi (margine sui timeout/504) e progress più fluida
$limit = 100;

// Inizializzazione del lettore CSV
$filepath = base_dir().'/files/'.$record->directory.'/'.$record->filename;
Expand Down
64 changes: 58 additions & 6 deletions modules/import/edit.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,44 @@
</div>
</form>';

// Totale record del CSV (intestazione inclusa): serve alla progress bar lato client
$totale_record = $csv->getTotalRows();

// La geolocalizzazione automatica (solo anagrafiche) fa una chiamata online per ogni riga:
// se attiva, l'importazione è più lenta e lo segnaliamo nell'overlay di avanzamento.
$geoloc_attiva = $import_selezionato === CSV::class && setting('Geolocalizzazione automatica');

echo '
<div id="import-progress-overlay" style="display:none; position:fixed; top:0; left:0; right:0; bottom:0; z-index:99999; background:rgba(255,255,255,0.92); flex-direction:column; justify-content:center; align-items:center;">
<div style="width:60%; max-width:600px;">
<h4 class="text-center" style="margin-bottom:15px;">'.tr('Importazione in corso...').'</h4>
<div class="progress" style="height:28px;">
<div id="import-progress-bar" class="progress-bar progress-bar-striped progress-bar-animated bg-primary" role="progressbar" style="width:0%; line-height:28px;">0%</div>
</div>
<div id="import-progress-label" class="text-center text-muted" style="margin-top:8px;"></div>
<div class="text-center" style="margin-top:18px; color:#b8860b; font-weight:600;">
<i class="fa fa-exclamation-triangle"></i> '.tr('Non chiudere né ricaricare questa pagina finché l\'importazione non è completata: interrompendola, alcune righe potrebbero non essere importate.').'
</div>'.($geoloc_attiva ? '
<div class="text-center" style="margin-top:12px; color:#6c757d;">
<i class="fa fa-clock-o"></i> '.tr('La geolocalizzazione automatica delle anagrafiche è attiva: ogni riga viene geolocalizzata tramite un servizio online, perciò l\'importazione può richiedere molto più tempo del normale.').'
</div>' : '').'
</div>
</div>';

echo '
<script>
var count = 0;
var failed_records_filename = null; // Variabile globale per mantener il nome del file di errore tra batch
var importTotalRecords = '.$totale_record.'; // record totali nel CSV (intestazione inclusa)
var importTotalRows = 0; // righe dati effettive da importare
var importProcessed = 0; // righe processate finora (importate + fallite)

function updateImportProgress() {
var pct = importTotalRows > 0 ? Math.min(100, Math.round(importProcessed / importTotalRows * 100)) : 0;
var done = Math.min(importProcessed, importTotalRows);
$("#import-progress-bar").css("width", pct + "%").text(pct + "%");
$("#import-progress-label").text(done + " / " + importTotalRows + " '.tr('righe').'");
}

$(document).ready(function() {';

Expand All @@ -256,6 +290,12 @@
save.on("click", function() {
count = 0;
failed_records_filename = null; // Reset della variabile

// Avanzamento: se la prima riga non viene importata è l\'intestazione, quindi va esclusa dal totale
importProcessed = 0;
importTotalRows = $("#include_first_row").is(":checked") ? importTotalRecords : Math.max(0, importTotalRecords - 1);
updateImportProgress();

importPage(0);
});
});
Expand All @@ -279,7 +319,7 @@ function importPage(page) {
}
}

$("#main_loading").show();
$("#import-progress-overlay").css("display", "flex");

let data = {
id_module: "'.$id_module.'",
Expand All @@ -302,7 +342,7 @@ function importPage(page) {

// Gestione errori di validazione
if (data.error) {
$("#main_loading").fadeOut();
$("#import-progress-overlay").hide();

Swal.fire({
title: "'.tr('Errore').'",
Expand All @@ -321,11 +361,17 @@ function importPage(page) {
// Aggiorna i contatori
count += data.imported;

// Avanzamento: somma le righe processate in questo batch (importate + fallite)
importProcessed += data.total;
updateImportProgress();

// Se ci sono altre pagine da importare
if(data.more) {
importPage(page + 1);
} else {
$("#main_loading").fadeOut();
importProcessed = importTotalRows;
updateImportProgress();
$("#import-progress-overlay").hide();

// Prepara il messaggio di completamento
let title = "'.tr('Importazione completata').'";
Expand Down Expand Up @@ -399,10 +445,16 @@ function importPage(page) {
}
}
},
error: function(data) {
$("#main_loading").fadeOut();
error: function(xhr) {
$("#import-progress-overlay").hide();

var message = (xhr && xhr.responseJSON && xhr.responseJSON.message) || (xhr && xhr.responseText) || (xhr && xhr.statusText) || "'.tr('Errore sconosciuto').'";

alert("'.tr('Errore').': " + data);
Swal.fire({
title: "'.tr('Errore').'",
text: message,
icon: "error",
});
}
});
};
Expand Down
26 changes: 18 additions & 8 deletions src/Importer/CSVImporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
namespace Importer;

use League\Csv\Reader;
use League\Csv\Statement;

/**
* Classe dedicata alla gestione dell'importazione da file CSV.
Expand Down Expand Up @@ -96,17 +97,24 @@ public function getHeader()
return array_shift($first_row);
}

/**
* Numero totale di record presenti nel file CSV (intestazione inclusa).
* Usato dalla UI per calcolare la percentuale di avanzamento dell'importazione.
*/
public function getTotalRows()
{
return count($this->csv);
}

public function getRows($offset, $length)
{
$rows = [];
for ($i = 0; $i < $length; ++$i) {
// Lettura di una singola riga alla volta
$row = $this->csv->fetchOne($offset + $i);
if (empty($row)) {
break;
}

// Aggiunta all'insieme dei record
// Lettura del blocco in un'unica passata sequenziale.
// NB: chiamare fetchOne($offset + $i) in un ciclo è O(n²), perché league/csv
// ri-scorre il file dall'inizio ad ogni chiamata; Statement lo scorre una sola volta.
$statement = Statement::create()->offset((int) $offset)->limit((int) $length);
foreach ($statement->process($this->csv) as $row) {
$rows[] = \Filter::parse($row);
}

Expand Down Expand Up @@ -170,8 +178,10 @@ public function importRows($offset, $length, $update_record = true, $add_record
$validated_records[] = $record;
}

$batch_size = 100;
$import_failed = 0;

// Autocommit per riga (volutamente NON in un'unica transazione per batch): così i lock
// restano brevissimi e un import lento/abbandonato non blocca l'intera applicazione.
foreach ($validated_records as $index => $record) {
$row = $validated_rows[$index];

Expand Down
Loading