Subversion Repositories eFlore/Applications.cel

Compare Revisions

Regard whitespace Rev 1935 → Rev 1936

/branches/v1.7-croissant/jrest/services/ImportXLS.php
38,6 → 38,10
//define('NB_LIRE_LIGNE_SIMUL', 30);
define('NB_LIRE_LIGNE_SIMUL', 5);
 
// en cas d'import d'un fichier CSV, utilise fgetcsv() plutôt
// que PHPExcel ce qui se traduit par un gain de performances très substanciel
define('QUICK_CSV_IMPORT', TRUE);
 
// Numbers of days between January 1, 1900 and 1970 (including 19 leap years)
// see traiterDateObs()
// define("MIN_DATES_DIFF", 25569);
152,6 → 156,8
// erreurs d'import
public $bilan = Array();
 
// cache (pour traiterLocalisation() pour l'instant)
static $cache = Array('geo' => array());
 
function ImportXLS($config) {
parent::__construct($config);
204,7 → 210,14
$objReader->setReadDataOnly(true);
 
// TODO: is_a obsolete entre 5.0 et 5.3, retirer le @ à terme
if(@is_a($objReader, 'PHPExcel_Reader_CSV')) {
$IS_CSV = @is_a($objReader, 'PHPExcel_Reader_CSV') && QUICK_CSV_IMPORT;
// en cas d'usage de fgetcsv, testons que nous pouvons compter les lignes
if($IS_CSV) $nb_lignes = intval(exec("wc -l $fichier"));
// et, le cas échéant, fallback sur PHPExcel à nouveau. La raison de ce test ici est
// l'instabilité du serveur (safe_mode, safe_mode_exec_dir, symlink vers binaires pour exec(), ... multiples points-of-failure)
if($IS_CSV && !$nb_lignes) $IS_CSV = FALSE;
 
if($IS_CSV) {
$objReader->setDelimiter(',')
->setEnclosure('"')
->setLineEnding("\n")
218,11 → 231,22
 
$objPHPExcel = $objReader->load($fichier);
$obj_infos = $objReader->listWorksheetInfo($fichier);
 
if($IS_CSV) {
// $nb_lignes est déjà défini ci-dessus
$csvFileHandler = fopen($fichier, 'r');
// nous utilisons la valeur de retour dans un but informatif de l'utilisateur à la
// fin de l'import, *mais aussi* dans un array_diff_key() ci-dessous car bien que dans le
// fond le "parser" fgetcsv() n'ait pas d'intérêt à connaître les colonnes à ignorer,
// il se trouve que celles-ci peuvent interférer sur des fonctions comme traiterEspece()
// cf test "ref-nom-num.test.php" pour lequel l'élément C_NOM_SEL vaudrait 3 et $ligne serait array(3 => -42)
$filtre->exclues = self::detectionEntete(fgetcsv($csvFileHandler), TRUE);
} else {
// XXX: indépendant du readFilter ?
$nb_lignes = $obj_infos[0]['totalRows'];
 
$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, TRUE, TRUE);
$filtre->exclues = self::detectionEntete($donnees[1]);
}
 
$obs_ajouts = 0;
$obs_maj = 0;
235,10 → 259,13
 
// on catch to les trigger_error(E_USER_NOTICE);
set_error_handler(array($this, 'erreurs_stock'), E_USER_NOTICE);
$this->taxon_info_webservice = new RechercheInfosTaxonBeta($this->config, NULL);
 
 
// lecture par morceaux (chunks), NB_LIRE_LIGNE_SIMUL lignes à fois
// pour aboutir des requêtes SQL d'insert groupés.
for($ligne = 2; $ligne < $nb_lignes + NB_LIRE_LIGNE_SIMUL; $ligne += NB_LIRE_LIGNE_SIMUL) {
if(!$IS_CSV) {
$filtre->def_interval($ligne, NB_LIRE_LIGNE_SIMUL);
$objReader->setReadFilter($filtre);
 
256,12 → 283,23
$objPHPExcel->getStyle(C_ZONE_GEO . '2:' . C_ZONE_GEO . $objPHPExcel->getHighestRow())->getNumberFormat()->setFormatCode('00000');
 
$donnees = $objPHPExcel->toArray(NULL, FALSE, TRUE, TRUE);
}
else {
$i = NB_LIRE_LIGNE_SIMUL;
$donnees = array();
while($i--) {
$tab = fgetcsv($csvFileHandler);
if(!$tab) continue;
$donnees[] = array_diff_key($tab, $filtre->exclues);
}
 
}
 
// var_dump($donnees, get_defined_constants(true)['user']);die;
// ici on appel la fonction qui fera effectivement l'insertion multiple
// à partir des (au plus) NB_LIRE_LIGNE_SIMUL lignes
 
// TODO: passer $this, ne sert que pour appeler des méthodes publiques qui pourraient être statiques
// notamment dans RechercheInfosTaxonBeta.php
list($enregistrements, $images, $mots_cle) =
self::chargerLignes($this, $donnees, $this->colonnes_statiques, $dernier_ordre);
if(! $enregistrements) break;
284,7 → 322,7
$donnees = array();
foreach($enregistrements as $e) $donnees = array_merge($donnees, array_values($e));
 
/* debug ici: echo $sql_pattern . "\n"; var_dump($enregistrements, $donnees); die;*/
// echo $sql_pattern . "\n"; var_dump($enregistrements, $donnees); die; // debug ici
 
$stmt->execute($donnees);
 
320,7 → 358,18
die();
}
 
static function detectionEntete($entete) {
/* detectionEntete() sert deux rôles:
1) détecter le type de colonne attendu à partir des textes de la ligne d'en-tête afin de define()
2) permet d'identifier les colonnes non-supportées/inutiles afin d'alléger le processus de parsing de PHPExcel
grace au ReadFilter (C'est le rôle de la valeur de retour)
 
La raison de la présence du paramètre $numeric_keys est que pour réussir à identifier les colonnes à exclure nous
devons traiter un tableau représentant la ligne d'en-tête aussi bien:
- sous forme associative pour PHPExcel (les clefs sont les lettres de l'alphabet)
- sous forme de clefs numériques (fgetcsv())
Le détecter après coup est difficile et pourtant cette distinction est importante car le comportement
d'array_merge() (réordonnancement des clefs numérique) n'est pas souhaitable dans le second cas. */
static function detectionEntete($entete, $numeric_keys = FALSE) {
$colonnes_reconnues = Array();
$cols = FormateurGroupeColonne::nomEnsembleVersListeColonnes('standard,avance');
foreach($entete as $k => $v) {
363,6 → 412,9
// ==> Array ( S => Ordre, AA => Phénologie )
$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
 
if($numeric_keys) {
return $colonnesID_non_reconnues + $colonnesID_a_exclure;
}
// TODO: pourquoi ne pas comparer avec les abbrevs aussi ?
// merge ( Array( I => rien ) , Array ( S => Ordre, AA => Phénologie ) )
// ==> Array ( I => rien, AA => Phénologie )
379,6 → 431,9
$tous_mots_cle = Array();
 
foreach($lignes as $ligne) {
// dans le cas de fgetcsv, on peut avoir des false additionnel (cf do/while l. 279)
if($ligne === false) continue;
 
//$ligne = array_filter($ligne, function($cell) { return !is_null($cell); });
//if(!$ligne) continue;
// on a besoin des NULL pour éviter des notice d'index indéfini
480,7 → 535,7
// $espece est rempli de plusieurs informations
$espece = Array(C_NOM_SEL => NULL, C_NOM_SEL_NN => NULL, C_NOM_RET => NULL,
C_NOM_RET_NN => NULL, C_NT => NULL, C_FAMILLE => NULL);
self::traiterEspece($ligne, $espece, $referentiel, $cel);
self::traiterEspece($ligne, $espece, $referentiel, $cel->taxon_info_webservice);
 
if(!$espece[C_NOM_SEL]) $referentiel = Cel::$fallback_referentiel;
if($espece[C_NOM_SEL] && !$espece[C_NOM_SEL_NN]) $referentiel = Cel::$fallback_referentiel;
690,7 → 745,7
TODO: s'affranchir du webservice pour la détermination du nom scientifique en s'appuyant sur cel_references,
pour des questions de performances
*/
static function traiterEspece($ligne, Array &$espece, &$referentiel, $cel) {
static function traiterEspece($ligne, Array &$espece, &$referentiel, $taxon_info_webservice) {
if(empty($ligne[C_NOM_SEL])) {
// TODO: nous ne déclarons pas "Numéro nomenclatural" comme colonne importable
// Nous ne pouvons donc pas tenter d'être sympa sur la détermination par num_nom
705,7 → 760,7
 
// XXX/attention, nous ne devrions pas accepter un référentiel absent !
if(!$referentiel) $referentiel = 'bdtfx';
$taxon_info_webservice = new RechercheInfosTaxonBeta($cel->config, $referentiel);
$taxon_info_webservice->setReferentiel($referentiel);
$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
 
// TODO: si empty(C_NOM_SEL) et !empty(C_NOM_SEL_NN) : recherche info à partir de C_NOM_SEL_NN
811,11 → 866,18
$identifiant_commune = trim($ligne[C_ZONE_GEO]);
if(!$identifiant_commune) {
$departement = trim($ligne[C_CE_ZONE_GEO]);
 
if(strpos($departement, "INSEE-C:", 0) === 0) {
$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
if(array_key_exists($localisation[C_CE_ZONE_GEO], self::$cache['geo'])) {
$localisation[C_ZONE_GEO] = self::$cache['geo'][$localisation[C_CE_ZONE_GEO]];
}
else {
$nom = Cel::db()->requeter(sprintf("SELECT nom FROM cel_zones_geo WHERE code = %s LIMIT 1",
self::quoteNonNull(substr($localisation[C_CE_ZONE_GEO], strlen("INSEE-C:")))));
if($nom) $localisation[C_ZONE_GEO] = $nom[0]['nom'];
self::$cache['geo'][$localisation[C_CE_ZONE_GEO]] = @$nom[0]['nom'];
}
return;
}
 
824,10 → 886,21
return;
}
 
if( ($resultat_commune = Cel::db()->requeter(sprintf("SELECT DISTINCT nom, CONCAT('INSEE-C:', code) AS code FROM cel_zones_geo WHERE code = %s LIMIT 1",
$cache_attempted = FALSE;
if(array_key_exists($departement, self::$cache['geo'])) {
$cache_attempted = TRUE;
if(self::$cache['geo'][$departement][0] && self::$cache['geo'][$departement][1]) {
$localisation[C_ZONE_GEO] = self::$cache['geo'][$departement][0];
$localisation[C_CE_ZONE_GEO] = self::$cache['geo'][$departement][1];
return;
}
}
 
if(! $cache_attempted && ($resultat_commune = Cel::db()->requeter(sprintf("SELECT DISTINCT nom, CONCAT('INSEE-C:', code) AS code FROM cel_zones_geo WHERE code = %s LIMIT 1",
self::quoteNonNull($departement)))) ) {
$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
self::$cache['geo'][$departement] = array($resultat_commune[0]['nom'], $resultat_commune[0]['code']);
return;
}
;
871,7 → 944,14
$requete = sprintf("%s WHERE nom LIKE %s", $select, self::quoteNonNull($nom_commune.'%'));
}
 
if(array_key_exists($identifiant_commune, self::$cache['geo'])) {
$resultat_commune = self::$cache['geo'][$identifiant_commune];
}
else {
$resultat_commune = Cel::db()->requeter($requete);
self::$cache['geo'][$identifiant_commune] = $resultat_commune;
}
// TODO: levenstein sort ?
// TODO: count résultat !