Line 36... |
Line 36... |
36 |
// nombre d'INSERT à cumuler par requête SQL
|
36 |
// nombre d'INSERT à cumuler par requête SQL
|
37 |
// (= nombre de lignes XLS à bufferiser)
|
37 |
// (= nombre de lignes XLS à bufferiser)
|
38 |
//define('NB_LIRE_LIGNE_SIMUL', 30);
|
38 |
//define('NB_LIRE_LIGNE_SIMUL', 30);
|
39 |
define('NB_LIRE_LIGNE_SIMUL', 5);
|
39 |
define('NB_LIRE_LIGNE_SIMUL', 5);
|
Line -... |
Line 40... |
- |
|
40 |
|
- |
|
41 |
// en cas d'import d'un fichier CSV, utilise fgetcsv() plutôt
|
- |
|
42 |
// que PHPExcel ce qui se traduit par un gain de performances très substanciel
|
- |
|
43 |
define('QUICK_CSV_IMPORT', TRUE);
|
40 |
|
44 |
|
41 |
// Numbers of days between January 1, 1900 and 1970 (including 19 leap years)
|
45 |
// Numbers of days between January 1, 1900 and 1970 (including 19 leap years)
|
42 |
// see traiterDateObs()
|
46 |
// see traiterDateObs()
|
Line 204... |
Line 208... |
204 |
$objReader = PHPExcel_IOFactory::createReaderForFile($fichier);
|
208 |
$objReader = PHPExcel_IOFactory::createReaderForFile($fichier);
|
205 |
// TODO: check if compatible with toArray(<1>,<2>,TRUE,<4>)
|
209 |
// TODO: check if compatible with toArray(<1>,<2>,TRUE,<4>)
|
206 |
$objReader->setReadDataOnly(true);
|
210 |
$objReader->setReadDataOnly(true);
|
Line 207... |
Line 211... |
207 |
|
211 |
|
208 |
// TODO: is_a obsolete entre 5.0 et 5.3, retirer le @ à terme
|
212 |
// TODO: is_a obsolete entre 5.0 et 5.3, retirer le @ à terme
|
- |
|
213 |
$IS_CSV = @is_a($objReader, 'PHPExcel_Reader_CSV') && QUICK_CSV_IMPORT;
|
- |
|
214 |
// en cas d'usage de fgetcsv, testons que nous pouvons compter les lignes
|
- |
|
215 |
if($IS_CSV) $nb_lignes = intval(exec("wc -l $fichier"));
|
- |
|
216 |
// et, le cas échéant, fallback sur PHPExcel à nouveau. La raison de ce test ici est
|
- |
|
217 |
// l'instabilité du serveur (safe_mode, safe_mode_exec_dir, symlink vers binaires pour exec(), ... multiples points-of-failure)
|
- |
|
218 |
if($IS_CSV && !$nb_lignes) $IS_CSV = FALSE;
|
- |
|
219 |
|
209 |
if(@is_a($objReader, 'PHPExcel_Reader_CSV')) {
|
220 |
if($IS_CSV) {
|
210 |
$objReader->setDelimiter(',')
|
221 |
$objReader->setDelimiter(',')
|
211 |
->setEnclosure('"')
|
222 |
->setEnclosure('"')
|
212 |
->setLineEnding("\n")
|
223 |
->setLineEnding("\n")
|
213 |
->setSheetIndex(0);
|
224 |
->setSheetIndex(0);
|
Line 218... |
Line 229... |
218 |
$filtre->def_interval(1, 2);
|
229 |
$filtre->def_interval(1, 2);
|
219 |
$objReader->setReadFilter($filtre);
|
230 |
$objReader->setReadFilter($filtre);
|
Line 220... |
Line 231... |
220 |
|
231 |
|
221 |
$objPHPExcel = $objReader->load($fichier);
|
232 |
$objPHPExcel = $objReader->load($fichier);
|
222 |
$obj_infos = $objReader->listWorksheetInfo($fichier);
|
- |
|
223 |
// XXX: indépendant du readFilter ?
|
- |
|
Line -... |
Line 233... |
- |
|
233 |
$obj_infos = $objReader->listWorksheetInfo($fichier);
|
- |
|
234 |
|
- |
|
235 |
if($IS_CSV) {
|
- |
|
236 |
// $nb_lignes est déjà défini ci-dessus
|
- |
|
237 |
$csvFileHandler = fopen($fichier, 'r');
|
- |
|
238 |
// nous utilisons la valeur de retour dans un but informatif de l'utilisateur à la
|
- |
|
239 |
// fin de l'import, *mais aussi* dans un array_diff_key() ci-dessous car bien que dans le
|
- |
|
240 |
// fond le "parser" fgetcsv() n'ait pas d'intérêt à connaître les colonnes à ignorer,
|
- |
|
241 |
// il se trouve que celles-ci peuvent interférer sur des fonctions comme traiterEspece()
|
- |
|
242 |
// cf test "ref-nom-num.test.php" pour lequel l'élément C_NOM_SEL vaudrait 3 et $ligne serait array(3 => -42)
|
- |
|
243 |
$filtre->exclues = self::detectionEntete(fgetcsv($csvFileHandler), TRUE);
|
- |
|
244 |
} else {
|
224 |
$nb_lignes = $obj_infos[0]['totalRows'];
|
245 |
// XXX: indépendant du readFilter ?
|
225 |
|
246 |
$nb_lignes = $obj_infos[0]['totalRows'];
|
- |
|
247 |
$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, TRUE, TRUE);
|
Line 226... |
Line 248... |
226 |
$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, TRUE, TRUE);
|
248 |
$filtre->exclues = self::detectionEntete($donnees[1]);
|
227 |
$filtre->exclues = self::detectionEntete($donnees[1]);
|
249 |
}
|
228 |
|
250 |
|
229 |
$obs_ajouts = 0;
|
251 |
$obs_ajouts = 0;
|
Line 235... |
Line 257... |
235 |
$dernier_ordre = intval($dernier_ordre[0]['ordre']) + 1;
|
257 |
$dernier_ordre = intval($dernier_ordre[0]['ordre']) + 1;
|
236 |
if(! $dernier_ordre) $dernier_ordre = 0;
|
258 |
if(! $dernier_ordre) $dernier_ordre = 0;
|
Line 237... |
Line 259... |
237 |
|
259 |
|
238 |
// on catch to les trigger_error(E_USER_NOTICE);
|
260 |
// on catch to les trigger_error(E_USER_NOTICE);
|
- |
|
261 |
set_error_handler(array($this, 'erreurs_stock'), E_USER_NOTICE);
|
- |
|
262 |
$this->taxon_info_webservice = new RechercheInfosTaxonBeta($this->config, NULL);
|
Line 239... |
Line 263... |
239 |
set_error_handler(array($this, 'erreurs_stock'), E_USER_NOTICE);
|
263 |
|
240 |
|
264 |
|
241 |
// lecture par morceaux (chunks), NB_LIRE_LIGNE_SIMUL lignes à fois
|
265 |
// lecture par morceaux (chunks), NB_LIRE_LIGNE_SIMUL lignes à fois
|
- |
|
266 |
// pour aboutir des requêtes SQL d'insert groupés.
|
242 |
// pour aboutir des requêtes SQL d'insert groupés.
|
267 |
for($ligne = 2; $ligne < $nb_lignes + NB_LIRE_LIGNE_SIMUL; $ligne += NB_LIRE_LIGNE_SIMUL) {
|
243 |
for($ligne = 2; $ligne < $nb_lignes + NB_LIRE_LIGNE_SIMUL; $ligne += NB_LIRE_LIGNE_SIMUL) {
|
268 |
if(!$IS_CSV) {
|
- |
|
269 |
$filtre->def_interval($ligne, NB_LIRE_LIGNE_SIMUL);
|
- |
|
270 |
$objReader->setReadFilter($filtre);
|
- |
|
271 |
|
- |
|
272 |
/* recharge avec $filtre actif (filtre sur lignes colonnes):
|
- |
|
273 |
- exclue les colonnes inutiles/inutilisables)
|
- |
|
274 |
- ne selectionne que les lignes dans le range [$ligne - $ligne + NB_LIRE_LIGNE_SIMUL] */
|
- |
|
275 |
$objPHPExcel = $objReader->load($fichier)->getActiveSheet();
|
- |
|
276 |
|
- |
|
277 |
// set col typing
|
- |
|
278 |
if(C_CE_ZONE_GEO != 'C_CE_ZONE_GEO')
|
- |
|
279 |
$objPHPExcel->getStyle(C_CE_ZONE_GEO . '2:' . C_CE_ZONE_GEO . $objPHPExcel->getHighestRow())->getNumberFormat()->setFormatCode('00000');
|
- |
|
280 |
|
- |
|
281 |
// TODO: set to string type
|
Line 244... |
Line -... |
244 |
$filtre->def_interval($ligne, NB_LIRE_LIGNE_SIMUL);
|
- |
|
245 |
$objReader->setReadFilter($filtre);
|
- |
|
246 |
|
- |
|
247 |
/* recharge avec $filtre actif (filtre sur lignes colonnes):
|
282 |
if(C_ZONE_GEO != 'C_ZONE_GEO')
|
248 |
- exclue les colonnes inutiles/inutilisables)
|
283 |
$objPHPExcel->getStyle(C_ZONE_GEO . '2:' . C_ZONE_GEO . $objPHPExcel->getHighestRow())->getNumberFormat()->setFormatCode('00000');
|
249 |
- ne selectionne que les lignes dans le range [$ligne - $ligne + NB_LIRE_LIGNE_SIMUL] */
|
284 |
|
250 |
$objPHPExcel = $objReader->load($fichier)->getActiveSheet();
|
285 |
$donnees = $objPHPExcel->toArray(NULL, FALSE, TRUE, TRUE);
|
251 |
|
286 |
}
|
252 |
// set col typing
|
287 |
else {
|
253 |
if(C_CE_ZONE_GEO != 'C_CE_ZONE_GEO')
|
288 |
$i = NB_LIRE_LIGNE_SIMUL;
|
254 |
$objPHPExcel->getStyle(C_CE_ZONE_GEO . '2:' . C_CE_ZONE_GEO . $objPHPExcel->getHighestRow())->getNumberFormat()->setFormatCode('00000');
|
289 |
$donnees = array();
|
255 |
|
290 |
while($i--) {
|
- |
|
291 |
$tab = fgetcsv($csvFileHandler);
|
Line 256... |
Line 292... |
256 |
// TODO: set to string type
|
292 |
if(!$tab) continue;
|
Line -... |
Line 293... |
- |
|
293 |
$donnees[] = array_diff_key($tab, $filtre->exclues);
|
257 |
if(C_ZONE_GEO != 'C_ZONE_GEO')
|
294 |
}
|
258 |
$objPHPExcel->getStyle(C_ZONE_GEO . '2:' . C_ZONE_GEO . $objPHPExcel->getHighestRow())->getNumberFormat()->setFormatCode('00000');
|
295 |
|
Line 259... |
Line 296... |
259 |
|
296 |
}
|
260 |
$donnees = $objPHPExcel->toArray(NULL, FALSE, TRUE, TRUE);
|
- |
|
261 |
|
297 |
|
262 |
// ici on appel la fonction qui fera effectivement l'insertion multiple
|
298 |
// var_dump($donnees, get_defined_constants(true)['user']);die;
|
263 |
// à partir des (au plus) NB_LIRE_LIGNE_SIMUL lignes
|
299 |
// ici on appel la fonction qui fera effectivement l'insertion multiple
|
Line 264... |
Line 300... |
264 |
|
300 |
// à partir des (au plus) NB_LIRE_LIGNE_SIMUL lignes
|
Line 284... |
Line 320... |
284 |
Cel::db()->beginTransaction();
|
320 |
Cel::db()->beginTransaction();
|
285 |
$stmt = Cel::db()->prepare($sql_pattern);
|
321 |
$stmt = Cel::db()->prepare($sql_pattern);
|
286 |
$donnees = array();
|
322 |
$donnees = array();
|
287 |
foreach($enregistrements as $e) $donnees = array_merge($donnees, array_values($e));
|
323 |
foreach($enregistrements as $e) $donnees = array_merge($donnees, array_values($e));
|
Line 288... |
Line 324... |
288 |
|
324 |
|
Line 289... |
Line 325... |
289 |
/* debug ici: echo $sql_pattern . "\n"; var_dump($enregistrements, $donnees); die;*/
|
325 |
// echo $sql_pattern . "\n"; var_dump($enregistrements, $donnees); die; // debug ici
|
Line 290... |
Line 326... |
290 |
|
326 |
|
291 |
$stmt->execute($donnees);
|
327 |
$stmt->execute($donnees);
|
Line 320... |
Line 356... |
320 |
count($filtre->exclues) > 1 ? 's' : '',
|
356 |
count($filtre->exclues) > 1 ? 's' : '',
|
321 |
implode(', ', $filtre->exclues));
|
357 |
implode(', ', $filtre->exclues));
|
322 |
die();
|
358 |
die();
|
323 |
}
|
359 |
}
|
Line -... |
Line 360... |
- |
|
360 |
|
- |
|
361 |
/* detectionEntete() sert deux rôles:
|
- |
|
362 |
1) détecter le type de colonne attendu à partir des textes de la ligne d'en-tête afin de define()
|
- |
|
363 |
2) permet d'identifier les colonnes non-supportées/inutiles afin d'alléger le processus de parsing de PHPExcel
|
- |
|
364 |
grace au ReadFilter (C'est le rôle de la valeur de retour)
|
- |
|
365 |
|
- |
|
366 |
La raison de la présence du paramètre $numeric_keys est que pour réussir à identifier les colonnes à exclure nous
|
- |
|
367 |
devons traiter un tableau représentant la ligne d'en-tête aussi bien:
|
- |
|
368 |
- sous forme associative pour PHPExcel (les clefs sont les lettres de l'alphabet)
|
- |
|
369 |
- sous forme de clefs numériques (fgetcsv())
|
- |
|
370 |
Le détecter après coup est difficile et pourtant cette distinction est importante car le comportement
|
324 |
|
371 |
d'array_merge() (réordonnancement des clefs numérique) n'est pas souhaitable dans le second cas. */
|
325 |
static function detectionEntete($entete) {
|
372 |
static function detectionEntete($entete, $numeric_keys = FALSE) {
|
326 |
$colonnes_reconnues = Array();
|
373 |
$colonnes_reconnues = Array();
|
327 |
$cols = FormateurGroupeColonne::nomEnsembleVersListeColonnes('standard,avance');
|
374 |
$cols = FormateurGroupeColonne::nomEnsembleVersListeColonnes('standard,avance');
|
328 |
foreach($entete as $k => $v) {
|
375 |
foreach($entete as $k => $v) {
|
329 |
// traite les colonnes en faisant fi de la casse et des accents
|
376 |
// traite les colonnes en faisant fi de la casse et des accents
|
Line 363... |
Line 410... |
363 |
|
410 |
|
364 |
// intersect ( Array ( N => Milieu, S => Ordre ), Array ( ordre => Ordre, phenologie => Phénologie ) )
|
411 |
// intersect ( Array ( N => Milieu, S => Ordre ), Array ( ordre => Ordre, phenologie => Phénologie ) )
|
365 |
// ==> Array ( S => Ordre, AA => Phénologie )
|
412 |
// ==> Array ( S => Ordre, AA => Phénologie )
|
Line -... |
Line 413... |
- |
|
413 |
$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
|
- |
|
414 |
|
- |
|
415 |
if($numeric_keys) {
|
366 |
$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
|
416 |
return $colonnesID_non_reconnues + $colonnesID_a_exclure;
|
367 |
|
417 |
}
|
368 |
// TODO: pourquoi ne pas comparer avec les abbrevs aussi ?
|
418 |
// TODO: pourquoi ne pas comparer avec les abbrevs aussi ?
|
369 |
// merge ( Array( I => rien ) , Array ( S => Ordre, AA => Phénologie ) )
|
419 |
// merge ( Array( I => rien ) , Array ( S => Ordre, AA => Phénologie ) )
|
370 |
// ==> Array ( I => rien, AA => Phénologie )
|
420 |
// ==> Array ( I => rien, AA => Phénologie )
|
Line 379... |
Line 429... |
379 |
$enregistrements = Array();
|
429 |
$enregistrements = Array();
|
380 |
$toutes_images = Array();
|
430 |
$toutes_images = Array();
|
381 |
$tous_mots_cle = Array();
|
431 |
$tous_mots_cle = Array();
|
Line 382... |
Line 432... |
382 |
|
432 |
|
- |
|
433 |
foreach($lignes as $ligne) {
|
- |
|
434 |
// dans le cas de fgetcsv, on peut avoir des false additionnel (cf do/while l. 279)
|
- |
|
435 |
if($ligne === false) continue;
|
383 |
foreach($lignes as $ligne) {
|
436 |
|
384 |
//$ligne = array_filter($ligne, function($cell) { return !is_null($cell); });
|
437 |
//$ligne = array_filter($ligne, function($cell) { return !is_null($cell); });
|
385 |
//if(!$ligne) continue;
|
438 |
//if(!$ligne) continue;
|
386 |
// on a besoin des NULL pour éviter des notice d'index indéfini
|
439 |
// on a besoin des NULL pour éviter des notice d'index indéfini
|