Subversion Repositories eFlore/Applications.cel

Rev

Rev 1929 | Rev 2034 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1929 Rev 1933
1
<?php
1
<?php
2
/**
2
/**
3
 * @category  PHP
3
 * @category  PHP
4
 * @package   jrest
4
 * @package   jrest
5
 * @author    Raphaël Droz <raphael@tela-botania.org>
5
 * @author    Raphaël Droz <raphael@tela-botania.org>
6
 * @copyright 2013 Tela-Botanica
6
 * @copyright 2013 Tela-Botanica
7
 * @license   http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
7
 * @license   http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
8
 * @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
8
 * @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
9
 */
9
 */
10
 
10
 
11
/**
11
/**
12
 * Service d'import de données d'observation du CEL au format XLS
12
 * Service d'import de données d'observation du CEL au format XLS
13
 *
13
 *
14
 * Sont define()'d commme n° de colonne tous les abbrevs retournés par
14
 * Sont define()'d commme n° de colonne tous les abbrevs retournés par
15
 * FormateurGroupeColonne::nomEnsembleVersListeColonnes() préfixés par C_  cf: detectionEntete()
15
 * FormateurGroupeColonne::nomEnsembleVersListeColonnes() préfixés par C_  cf: detectionEntete()
16
 *
16
 *
17
 * Exemple d'un test:
17
 * Exemple d'un test:
18
 * $ GET "/jrest/ExportXLS/22506?format=csv&range=*&limite=13" \
18
 * $ GET "/jrest/ExportXLS/22506?format=csv&range=*&limite=13" \
19
 *   | curl -F "upload=@-" -F utilisateur=22506 "/jrest/ImportXLS"
19
 *   | curl -F "upload=@-" -F utilisateur=22506 "/jrest/ImportXLS"
20
 * # 13 observations importées
20
 * # 13 observations importées
21
 * + cf MySQL general_log = 1
21
 * + cf MySQL general_log = 1
22
 *  
22
 *  
23
 **/
23
 **/
24
 
24
 
25
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(realpath(__FILE__))) . '/lib');
25
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(realpath(__FILE__))) . '/lib');
26
// TERM
26
// TERM
27
error_reporting(-1);
27
error_reporting(-1);
28
ini_set('html_errors', 0);
28
ini_set('html_errors', 0);
29
ini_set('xdebug.cli_color', 2);
29
ini_set('xdebug.cli_color', 2);
30
require_once('lib/PHPExcel/Classes/PHPExcel.php');
30
require_once('lib/PHPExcel/Classes/PHPExcel.php');
31
require_once('FormateurGroupeColonne.php');
31
require_once('FormateurGroupeColonne.php');
32
 
32
 
33
 
33
 
34
date_default_timezone_set("Europe/Paris");
34
date_default_timezone_set("Europe/Paris");
35
 
35
 
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);
-
 
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()
43
// define("MIN_DATES_DIFF", 25569);
47
// define("MIN_DATES_DIFF", 25569);
44
 
48
 
45
 
49
 
46
class MyReadFilter implements PHPExcel_Reader_IReadFilter {
50
class MyReadFilter implements PHPExcel_Reader_IReadFilter {
47
    // exclusion de colonnes
51
    // exclusion de colonnes
48
    public $exclues = array();
52
    public $exclues = array();
49
 
53
 
50
    // lecture par morceaux
54
    // lecture par morceaux
51
    public $ligne_debut = 0; 
55
    public $ligne_debut = 0; 
52
    public $ligne_fin = 0;
56
    public $ligne_fin = 0;
53
 
57
 
54
    public function __construct() {}
58
    public function __construct() {}
55
    public function def_interval($debut, $nb) {
59
    public function def_interval($debut, $nb) {
56
	$this->ligne_debut = $debut;
60
	$this->ligne_debut = $debut;
57
	$this->ligne_fin = $debut + $nb;
61
	$this->ligne_fin = $debut + $nb;
58
    }
62
    }
59
    public function readCell($colonne, $ligne, $worksheetName = '') {
63
    public function readCell($colonne, $ligne, $worksheetName = '') {
60
	if(@$this->exclues[$colonne]) return false;
64
	if(@$this->exclues[$colonne]) return false;
61
	// si des n° de morceaux ont été initialisés, on filtre...
65
	// si des n° de morceaux ont été initialisés, on filtre...
62
	if($this->ligne_debut && ($ligne < $this->ligne_debut || $ligne >= $this->ligne_fin)) return false;
66
	if($this->ligne_debut && ($ligne < $this->ligne_debut || $ligne >= $this->ligne_fin)) return false;
63
	return true;
67
	return true;
64
    } 
68
    } 
65
} 
69
} 
66
 
70
 
67
// XXX: PHP 5.3
71
// XXX: PHP 5.3
68
function __anonyme_1($v) {	return !$v['importable']; }
72
function __anonyme_1($v) {	return !$v['importable']; }
69
function __anonyme_2(&$v) {	$v = $v['nom']; }
73
function __anonyme_2(&$v) {	$v = $v['nom']; }
70
function __anonyme_3($cell) { return !is_null($cell); };
74
function __anonyme_3($cell) { return !is_null($cell); };
71
function __anonyme_5($item) { return is_null($item) ? '?' : $item; }
75
function __anonyme_5($item) { return is_null($item) ? '?' : $item; }
72
function __anonyme_6() { return NULL; }
76
function __anonyme_6() { return NULL; }
73
 
77
 
74
class ImportXLS extends Cel  {
78
class ImportXLS extends Cel  {
75
    static function __anonyme_4(&$item, $key) { $item = self::quoteNonNull(trim($item)); }
79
    static function __anonyme_4(&$item, $key) { $item = self::quoteNonNull(trim($item)); }
76
 
80
 
77
    static $ordre_BDD = Array(
81
    static $ordre_BDD = Array(
78
	"ce_utilisateur",
82
	"ce_utilisateur",
79
	"prenom_utilisateur",
83
	"prenom_utilisateur",
80
	"nom_utilisateur",
84
	"nom_utilisateur",
81
	"courriel_utilisateur",
85
	"courriel_utilisateur",
82
	"ordre",
86
	"ordre",
83
	"nom_sel",
87
	"nom_sel",
84
	"nom_sel_nn",
88
	"nom_sel_nn",
85
	"nom_ret",
89
	"nom_ret",
86
	"nom_ret_nn",
90
	"nom_ret_nn",
87
	"nt",
91
	"nt",
88
	"famille",
92
	"famille",
89
	"nom_referentiel",
93
	"nom_referentiel",
90
	"zone_geo",
94
	"zone_geo",
91
	"ce_zone_geo",
95
	"ce_zone_geo",
92
	"date_observation",
96
	"date_observation",
93
	"lieudit",
97
	"lieudit",
94
	"station",
98
	"station",
95
	"milieu",
99
	"milieu",
96
	"mots_cles_texte",
100
	"mots_cles_texte",
97
	"commentaire",
101
	"commentaire",
98
	"transmission",
102
	"transmission",
99
	"date_creation",
103
	"date_creation",
100
	"date_modification",
104
	"date_modification",
101
	"date_transmission",
105
	"date_transmission",
102
	"latitude",
106
	"latitude",
103
	"longitude",
107
	"longitude",
104
	"altitude",
108
	"altitude",
105
	"abondance",
109
	"abondance",
106
	"certitude",
110
	"certitude",
107
	"phenologie",
111
	"phenologie",
108
	"code_insee_calcule"
112
	"code_insee_calcule"
109
    );
113
    );
110
 
114
 
111
    // cf: initialiser_pdo_ordered_statements()
115
    // cf: initialiser_pdo_ordered_statements()
112
    // eg: "INSERT INTO cel_obs (ce_utilisateur, ..., phenologie, code_insee_calcule) VALUES"
116
    // eg: "INSERT INTO cel_obs (ce_utilisateur, ..., phenologie, code_insee_calcule) VALUES"
113
    // colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
117
    // colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
114
    static $insert_prefix_ordre;
118
    static $insert_prefix_ordre;
115
    // eg: "(<id>, <prenom>, <nom>, <email>, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
119
    // eg: "(<id>, <prenom>, <nom>, <email>, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
116
    // dont le nombre de placeholder dépend du nombre de colonnes non-statiques
120
    // dont le nombre de placeholder dépend du nombre de colonnes non-statiques
117
    // colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
121
    // colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
118
    static $insert_ligne_pattern_ordre;
122
    static $insert_ligne_pattern_ordre;
119
 
123
 
120
    // seconde (meilleure) possibilité
124
    // seconde (meilleure) possibilité
121
    // cf: initialiser_pdo_statements()
125
    // cf: initialiser_pdo_statements()
122
    // eg: "INSERT INTO cel_obs (ce_utilisateur, ..., date_creation, ...phenologie, code_insee_calcule) VALUES"
126
    // eg: "INSERT INTO cel_obs (ce_utilisateur, ..., date_creation, ...phenologie, code_insee_calcule) VALUES"
123
    static $insert_prefix;
127
    static $insert_prefix;
124
    // eg: "(<id>, <prenom>, <nom>, <email>, ?, ?, ?, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
128
    // eg: "(<id>, <prenom>, <nom>, <email>, ?, ?, ?, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
125
    // dont le nombre de placeholder dépend du nombre de colonnes non-statiques
129
    // dont le nombre de placeholder dépend du nombre de colonnes non-statiques
126
    static $insert_ligne_pattern;
130
    static $insert_ligne_pattern;
127
 
131
 
128
    /*
132
    /*
129
      Ces colonnes:
133
      Ces colonnes:
130
      - sont propres à l'ensemble des enregistrements uploadés
134
      - sont propres à l'ensemble des enregistrements uploadés
131
      - sont indépendantes du numéro de lignes
135
      - sont indépendantes du numéro de lignes
132
      - n'ont pas de valeur par défaut dans la structure de la table
136
      - n'ont pas de valeur par défaut dans la structure de la table
133
      - nécessitent une initialisation dans le cadre de l'upload
137
      - nécessitent une initialisation dans le cadre de l'upload
134
 
138
 
135
      initialiser_colonnes_statiques() y merge les données d'identification utilisateur
139
      initialiser_colonnes_statiques() y merge les données d'identification utilisateur
136
    */
140
    */
137
    public $colonnes_statiques = Array(
141
    public $colonnes_statiques = Array(
138
	"ce_utilisateur" => NULL,
142
	"ce_utilisateur" => NULL,
139
	"prenom_utilisateur" => NULL,
143
	"prenom_utilisateur" => NULL,
140
	"nom_utilisateur" => NULL,
144
	"nom_utilisateur" => NULL,
141
	"courriel_utilisateur" => NULL,
145
	"courriel_utilisateur" => NULL,
142
 
146
 
143
	// fixes (fonction SQL)
147
	// fixes (fonction SQL)
144
	// XXX future: mais pourraient varier dans le futur si la mise-à-jour
148
	// XXX future: mais pourraient varier dans le futur si la mise-à-jour
145
	// d'observation est implémentée
149
	// d'observation est implémentée
146
	"date_creation" => "now()",
150
	"date_creation" => "now()",
147
	"date_modification" => "now()",
151
	"date_modification" => "now()",
148
    );
152
    );
149
 
153
 
150
    public $id_utilisateur = NULL;
154
    public $id_utilisateur = NULL;
151
 
155
 
152
    // erreurs d'import
156
    // erreurs d'import
153
    public $bilan = Array();
157
    public $bilan = Array();
154
 
158
 
155
    // cache (pour traiterLocalisation() pour l'instant)
159
    // cache (pour traiterLocalisation() pour l'instant)
156
    static $cache = Array('geo' => array());
160
    static $cache = Array('geo' => array());
157
 
161
 
158
    function ImportXLS($config) {
162
    function ImportXLS($config) {
159
	parent::__construct($config);
163
	parent::__construct($config);
160
    }
164
    }
161
 
165
 
162
    function createElement($pairs) {
166
    function createElement($pairs) {
163
	if(!isset($pairs['utilisateur']) || trim($pairs['utilisateur']) == '') {
167
	if(!isset($pairs['utilisateur']) || trim($pairs['utilisateur']) == '') {
164
	    exit('0');
168
	    exit('0');
165
	}
169
	}
166
 
170
 
167
	$id_utilisateur = intval($pairs['utilisateur']);
171
	$id_utilisateur = intval($pairs['utilisateur']);
168
	$this->id_utilisateur = $id_utilisateur; // pour traiterImage();
172
	$this->id_utilisateur = $id_utilisateur; // pour traiterImage();
169
 
173
 
170
	if(!isset($_SESSION)) session_start();
174
	if(!isset($_SESSION)) session_start();
171
	$this->controleUtilisateur($id_utilisateur);
175
	$this->controleUtilisateur($id_utilisateur);
172
 
176
 
173
	$this->utilisateur = $this->getInfosComplementairesUtilisateur($id_utilisateur);
177
	$this->utilisateur = $this->getInfosComplementairesUtilisateur($id_utilisateur);
174
 
178
 
175
	$this->initialiser_colonnes_statiques($id_utilisateur);
179
	$this->initialiser_colonnes_statiques($id_utilisateur);
176
 
180
 
177
	// initialisation du statement PDO/MySQL
181
	// initialisation du statement PDO/MySQL
178
	// première version, pattern de requête pas génial
182
	// première version, pattern de requête pas génial
179
	/* list(self;;$insert_prefix_ordre, self::$insert_ligne_pattern_ordre) =
183
	/* list(self;;$insert_prefix_ordre, self::$insert_ligne_pattern_ordre) =
180
	   $this->initialiser_pdo_ordered_statements($this->colonnes_statiques); */
184
	   $this->initialiser_pdo_ordered_statements($this->colonnes_statiques); */
181
	list(self::$insert_prefix, self::$insert_ligne_pattern) =
185
	list(self::$insert_prefix, self::$insert_ligne_pattern) =
182
	    $this->initialiser_pdo_statements($this->colonnes_statiques);
186
	    $this->initialiser_pdo_statements($this->colonnes_statiques);
183
 
187
 
184
	$infos_fichier = array_pop($_FILES);
188
	$infos_fichier = array_pop($_FILES);
185
		
189
		
186
	/*$objPHPExcel = PHPExcel_IOFactory::load($infos_fichier['tmp_name']);
190
	/*$objPHPExcel = PHPExcel_IOFactory::load($infos_fichier['tmp_name']);
187
	  $donnees = $objPHPExcel->getActiveSheet()->toArray(NULL,FALSE,FALSE,TRUE);*/
191
	  $donnees = $objPHPExcel->getActiveSheet()->toArray(NULL,FALSE,FALSE,TRUE);*/
188
 
192
 
189
	/*$objReader = PHPExcel_IOFactory::createReader("Excel5");
193
	/*$objReader = PHPExcel_IOFactory::createReader("Excel5");
190
	  $objReader->setReadDataOnly(true);
194
	  $objReader->setReadDataOnly(true);
191
	  $objPHPExcel = $objReader->load($infos_fichier['tmp_name']);*/
195
	  $objPHPExcel = $objReader->load($infos_fichier['tmp_name']);*/
192
 
196
 
193
	//var_dump($donnees);
197
	//var_dump($donnees);
194
 
198
 
195
	// renomme le fichier pour lui ajouter son extension initiale, ce qui
199
	// renomme le fichier pour lui ajouter son extension initiale, ce qui
196
	// permet (une sorte) d'autodétection du format.
200
	// permet (une sorte) d'autodétection du format.
197
	$fichier = $infos_fichier['tmp_name'];
201
	$fichier = $infos_fichier['tmp_name'];
198
	$extension = pathinfo($infos_fichier['name'], PATHINFO_EXTENSION);
202
	$extension = pathinfo($infos_fichier['name'], PATHINFO_EXTENSION);
199
	if( (strlen($extension) == 3 || strlen($extension) == 4) &&
203
	if( (strlen($extension) == 3 || strlen($extension) == 4) &&
200
	    (@rename($fichier, $fichier . '.' . $extension))) { // XXX: @ safe-mode
204
	    (@rename($fichier, $fichier . '.' . $extension))) { // XXX: @ safe-mode
201
	    $fichier = $fichier . '.' . $extension;
205
	    $fichier = $fichier . '.' . $extension;
202
	}
206
	}
203
 
207
 
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);
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
209
	if(@is_a($objReader, 'PHPExcel_Reader_CSV')) {
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
 
-
 
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);
214
	}
225
	}
215
 
226
 
216
	// on ne conserve que l'en-tête
227
	// on ne conserve que l'en-tête
217
	$filtre = new MyReadFilter();
228
	$filtre = new MyReadFilter();
218
	$filtre->def_interval(1, 2);
229
	$filtre->def_interval(1, 2);
219
	$objReader->setReadFilter($filtre);
230
	$objReader->setReadFilter($filtre);
220
 
231
 
221
	$objPHPExcel = $objReader->load($fichier);
232
	$objPHPExcel = $objReader->load($fichier);
222
	$obj_infos = $objReader->listWorksheetInfo($fichier);
233
	$obj_infos = $objReader->listWorksheetInfo($fichier);
223
	// XXX: indépendant du readFilter ?
-
 
224
	$nb_lignes = $obj_infos[0]['totalRows'];
-
 
-
 
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 {
-
 
245
	    // XXX: indépendant du readFilter ?
225
 
246
	    $nb_lignes = $obj_infos[0]['totalRows'];
226
	$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, TRUE, TRUE);
247
	    $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;
230
	$obs_maj = 0;
252
	$obs_maj = 0;
231
	$nb_images_ajoutees = 0;
253
	$nb_images_ajoutees = 0;
232
	$nb_mots_cle_ajoutes = 0;
254
	$nb_mots_cle_ajoutes = 0;
233
 
255
 
234
	$dernier_ordre = Cel::db()->requeter("SELECT MAX(ordre) AS ordre FROM cel_obs WHERE ce_utilisateur = $id_utilisateur");
256
	$dernier_ordre = Cel::db()->requeter("SELECT MAX(ordre) AS ordre FROM cel_obs WHERE ce_utilisateur = $id_utilisateur");
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;
237
 
259
 
238
	// on catch to les trigger_error(E_USER_NOTICE);
260
	// on catch to les trigger_error(E_USER_NOTICE);
239
	set_error_handler(array($this, 'erreurs_stock'), E_USER_NOTICE);
261
	set_error_handler(array($this, 'erreurs_stock'), E_USER_NOTICE);
-
 
262
	$this->taxon_info_webservice = new RechercheInfosTaxonBeta($this->config, NULL);
-
 
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
242
	// pour aboutir des requêtes SQL d'insert groupés.
266
	// pour aboutir des requêtes SQL d'insert groupés.
243
	for($ligne = 2; $ligne < $nb_lignes + NB_LIRE_LIGNE_SIMUL; $ligne += NB_LIRE_LIGNE_SIMUL) {
267
	for($ligne = 2; $ligne < $nb_lignes + NB_LIRE_LIGNE_SIMUL; $ligne += NB_LIRE_LIGNE_SIMUL) {
-
 
268
	    if(!$IS_CSV) {
244
	    $filtre->def_interval($ligne, NB_LIRE_LIGNE_SIMUL);
269
		$filtre->def_interval($ligne, NB_LIRE_LIGNE_SIMUL);
245
	    $objReader->setReadFilter($filtre);
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
-
 
282
		if(C_ZONE_GEO != 'C_ZONE_GEO')
-
 
283
		    $objPHPExcel->getStyle(C_ZONE_GEO . '2:' . C_ZONE_GEO . $objPHPExcel->getHighestRow())->getNumberFormat()->setFormatCode('00000');
246
 
-
 
247
	    /* recharge avec $filtre actif (filtre sur lignes colonnes):
-
 
248
	       - exclue les colonnes inutiles/inutilisables)
-
 
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--) {
256
	    // TODO: set to string type
291
		    $tab = fgetcsv($csvFileHandler);
257
	    if(C_ZONE_GEO != 'C_ZONE_GEO')
292
		    if(!$tab) continue;
-
 
293
		    $donnees[] = array_diff_key($tab, $filtre->exclues);
258
		$objPHPExcel->getStyle(C_ZONE_GEO . '2:' . C_ZONE_GEO . $objPHPExcel->getHighestRow())->getNumberFormat()->setFormatCode('00000');
294
		}
-
 
295
 
259
 
296
	    }
260
	    $donnees = $objPHPExcel->toArray(NULL, FALSE, TRUE, TRUE);
297
 
261
 
298
	    // var_dump($donnees, get_defined_constants(true)['user']);die;
262
	    // ici on appel la fonction qui fera effectivement l'insertion multiple
299
	    // ici on appel la fonction qui fera effectivement l'insertion multiple
263
	    // à partir des (au plus) NB_LIRE_LIGNE_SIMUL lignes
300
	    // à partir des (au plus) NB_LIRE_LIGNE_SIMUL lignes
264
 
301
 
265
	    // TODO: passer $this, ne sert que pour appeler des méthodes publiques qui pourraient être statiques
302
	    // TODO: passer $this, ne sert que pour appeler des méthodes publiques qui pourraient être statiques
266
	    $this->taxon_info_webservice = new RechercheInfosTaxonBeta($this->config, NULL);
-
 
267
	    list($enregistrements, $images, $mots_cle) =
303
	    list($enregistrements, $images, $mots_cle) =
268
		self::chargerLignes($this, $donnees, $this->colonnes_statiques, $dernier_ordre);
304
		self::chargerLignes($this, $donnees, $this->colonnes_statiques, $dernier_ordre);
269
	    if(! $enregistrements) break;
305
	    if(! $enregistrements) break;
270
 
306
 
271
	    self::trierColonnes($enregistrements);
307
	    self::trierColonnes($enregistrements);
272
	    // normalement: NB_LIRE_LIGNE_SIMUL, sauf si une enregistrement ne semble pas valide
308
	    // normalement: NB_LIRE_LIGNE_SIMUL, sauf si une enregistrement ne semble pas valide
273
	    // ou bien lors du dernier chunk
309
	    // ou bien lors du dernier chunk
274
 
310
 
275
	    $nb_rec = count($enregistrements);
311
	    $nb_rec = count($enregistrements);
276
	    $sql_pattern = self::$insert_prefix .
312
	    $sql_pattern = self::$insert_prefix .
277
		str_repeat(self::$insert_ligne_pattern_ordre . ', ', $nb_rec - 1) .
313
		str_repeat(self::$insert_ligne_pattern_ordre . ', ', $nb_rec - 1) .
278
		self::$insert_ligne_pattern_ordre;
314
		self::$insert_ligne_pattern_ordre;
279
 
315
 
280
	    $sql_pattern = self::$insert_prefix .
316
	    $sql_pattern = self::$insert_prefix .
281
		str_repeat(self::$insert_ligne_pattern . ', ', $nb_rec - 1) .
317
		str_repeat(self::$insert_ligne_pattern . ', ', $nb_rec - 1) .
282
		self::$insert_ligne_pattern;
318
		self::$insert_ligne_pattern;
283
 
319
 
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));
288
 
324
 
289
	    /* debug ici: echo $sql_pattern . "\n"; var_dump($enregistrements, $donnees); die;*/
325
	    // echo $sql_pattern . "\n"; var_dump($enregistrements, $donnees); die; // debug ici
290
 
326
 
291
	    $stmt->execute($donnees);
327
	    $stmt->execute($donnees);
292
 
328
 
293
	    // $stmt->debugDumpParams(); // https://bugs.php.net/bug.php?id=52384
329
	    // $stmt->debugDumpParams(); // https://bugs.php.net/bug.php?id=52384
294
	    $dernier_autoinc = Cel::db()->lastInsertId();
330
	    $dernier_autoinc = Cel::db()->lastInsertId();
295
	    Cel::db()->commit();
331
	    Cel::db()->commit();
296
 
332
 
297
	    if(! $dernier_autoinc) trigger_error("l'insertion semble avoir échoué", E_USER_NOTICE);
333
	    if(! $dernier_autoinc) trigger_error("l'insertion semble avoir échoué", E_USER_NOTICE);
298
 
334
 
299
	    $obs_ajouts += count($enregistrements);
335
	    $obs_ajouts += count($enregistrements);
300
	    // $obs_ajouts += count($enregistrements['insert']);
336
	    // $obs_ajouts += count($enregistrements['insert']);
301
	    // $obs_maj += count($enregistrements['update']);
337
	    // $obs_maj += count($enregistrements['update']);
302
	    $nb_images_ajoutees += self::stockerImages($enregistrements, $images, $dernier_autoinc);
338
	    $nb_images_ajoutees += self::stockerImages($enregistrements, $images, $dernier_autoinc);
303
	    $nb_mots_cle_ajoutes += self::stockerMotsCle($enregistrements, $mots_cle, $dernier_autoinc);
339
	    $nb_mots_cle_ajoutes += self::stockerMotsCle($enregistrements, $mots_cle, $dernier_autoinc);
304
	}
340
	}
305
 
341
 
306
	restore_error_handler();
342
	restore_error_handler();
307
 
343
 
308
	if($this->bilan) echo implode("\n", $this->bilan) . "\n";
344
	if($this->bilan) echo implode("\n", $this->bilan) . "\n";
309
	printf('%1$d observation%2$s ajoutée%2$s' . "\n" .
345
	printf('%1$d observation%2$s ajoutée%2$s' . "\n" .
310
	       '%3$d image%4$s attachée%4$s' . "\n" .
346
	       '%3$d image%4$s attachée%4$s' . "\n" .
311
	       // '%5$d mot%6$c-clef ajouté%6$c [TODO]' . "\n" . // TODO
347
	       // '%5$d mot%6$c-clef ajouté%6$c [TODO]' . "\n" . // TODO
312
	       (count($filtre->exclues) > 0 ? 'colonne%7$s non-traitée%7$s: %8$s' . "\n" : ''),
348
	       (count($filtre->exclues) > 0 ? 'colonne%7$s non-traitée%7$s: %8$s' . "\n" : ''),
313
 
349
 
314
	       $obs_ajouts,
350
	       $obs_ajouts,
315
	       $obs_ajouts > 1 ? 's' : '',
351
	       $obs_ajouts > 1 ? 's' : '',
316
	       $nb_images_ajoutees,
352
	       $nb_images_ajoutees,
317
	       $nb_images_ajoutees > 1 ? 's' : '',
353
	       $nb_images_ajoutees > 1 ? 's' : '',
318
	       $nb_mots_cle_ajoutes,
354
	       $nb_mots_cle_ajoutes,
319
	       $nb_mots_cle_ajoutes > 1 ? 's' : '',
355
	       $nb_mots_cle_ajoutes > 1 ? 's' : '',
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
    }
-
 
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
330
	    $entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
377
	    $entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
331
	    foreach($cols as $col) {
378
	    foreach($cols as $col) {
332
		$entete_officiel_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($col['nom'])));
379
		$entete_officiel_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($col['nom'])));
333
		$entete_officiel_abbrev = $col['abbrev'];
380
		$entete_officiel_abbrev = $col['abbrev'];
334
		if($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
381
		if($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
335
		    // debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
382
		    // debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
336
		    define("C_" . strtoupper($entete_officiel_abbrev), $k);
383
		    define("C_" . strtoupper($entete_officiel_abbrev), $k);
337
		    $colonnes_reconnues[$k] = 1;
384
		    $colonnes_reconnues[$k] = 1;
338
		    break;
385
		    break;
339
		}
386
		}
340
	    }
387
	    }
341
	}
388
	}
342
	// défini tous les index que nous utilisons à une valeur d'index de colonne Excel qui n'existe pas dans
389
	// défini tous les index que nous utilisons à une valeur d'index de colonne Excel qui n'existe pas dans
343
	// le tableau renvoyé par PHPExcel
390
	// le tableau renvoyé par PHPExcel
344
	// Attention cependant d'utiliser des indexes différenciés car traiterLonLat() et traiterEspece()
391
	// Attention cependant d'utiliser des indexes différenciés car traiterLonLat() et traiterEspece()
345
	// les utilisent
392
	// les utilisent
346
	foreach($cols as $col) {
393
	foreach($cols as $col) {
347
	    if(!defined("C_" . strtoupper($col['abbrev'])))
394
	    if(!defined("C_" . strtoupper($col['abbrev'])))
348
		define("C_" . strtoupper($col['abbrev']), "C_" . strtoupper($col['abbrev']));
395
		define("C_" . strtoupper($col['abbrev']), "C_" . strtoupper($col['abbrev']));
349
	}
396
	}
350
 
397
 
351
	// prépare le filtre de PHPExcel qui évitera le traitement de toutes les colonnes superflues
398
	// prépare le filtre de PHPExcel qui évitera le traitement de toutes les colonnes superflues
352
 
399
 
353
	// eg: diff ( Array( H => Commune, I => rien ) , Array( H => 1, K => 1 )
400
	// eg: diff ( Array( H => Commune, I => rien ) , Array( H => 1, K => 1 )
354
	// ==> Array( I => rien )
401
	// ==> Array( I => rien )
355
	$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
402
	$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
356
 
403
 
357
	// des colonnes de FormateurGroupeColonne::nomEnsembleVersListeColonnes()
404
	// des colonnes de FormateurGroupeColonne::nomEnsembleVersListeColonnes()
358
	// ne retient que celles marquées "importables"
405
	// ne retient que celles marquées "importables"
359
	$colonnes_automatiques = array_filter($cols, '__anonyme_1');
406
	$colonnes_automatiques = array_filter($cols, '__anonyme_1');
360
 
407
 
361
	// ne conserve que le nom long pour matcher avec la ligne XLS d'entête
408
	// ne conserve que le nom long pour matcher avec la ligne XLS d'entête
362
	array_walk($colonnes_automatiques, '__anonyme_2');
409
	array_walk($colonnes_automatiques, '__anonyme_2');
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 )
366
	$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
413
	$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
-
 
414
 
-
 
415
	if($numeric_keys) {
-
 
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 )
371
	return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
421
	return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
372
    }
422
    }
373
 
423
 
374
    /*
424
    /*
375
     * charge un groupe de lignes
425
     * charge un groupe de lignes
376
     */
426
     */
377
    static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
427
    static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
378
	$enregistrement = NULL;
428
	$enregistrement = NULL;
379
	$enregistrements = Array();
429
	$enregistrements = Array();
380
	$toutes_images = Array();
430
	$toutes_images = Array();
381
	$tous_mots_cle = Array();
431
	$tous_mots_cle = Array();
382
 
432
 
383
	foreach($lignes as $ligne) {
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;
-
 
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
387
	    if(! array_filter($ligne, '__anonyme_3')) continue;
440
	    if(! array_filter($ligne, '__anonyme_3')) continue;
388
 
441
 
389
	    if( ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) ) {
442
	    if( ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) ) {
390
		// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
443
		// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
391
		$enregistrements[] = $enregistrement;
444
		$enregistrements[] = $enregistrement;
392
		$pos = count($enregistrements) - 1;
445
		$pos = count($enregistrements) - 1;
393
		$last = &$enregistrements[$pos];
446
		$last = &$enregistrements[$pos];
394
 
447
 
395
		if(isset($enregistrement['_images'])) {
448
		if(isset($enregistrement['_images'])) {
396
		    // ne dépend pas de cel_obs, et seront insérées *après* les enregistrements
449
		    // ne dépend pas de cel_obs, et seront insérées *après* les enregistrements
397
		    // mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
450
		    // mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
398
		    $toutes_images[] = Array("images" => $last['_images'],
451
		    $toutes_images[] = Array("images" => $last['_images'],
399
					     "obs_pos" => $pos);
452
					     "obs_pos" => $pos);
400
		    // ce champ n'a pas à faire partie de l'insertion dans cel_obs,
453
		    // ce champ n'a pas à faire partie de l'insertion dans cel_obs,
401
		    // mais est utile pour cel_obs_images
454
		    // mais est utile pour cel_obs_images
402
		    unset($last['_images']);
455
		    unset($last['_images']);
403
		}
456
		}
404
 
457
 
405
		if(isset($enregistrement['_mots_cle'])) {
458
		if(isset($enregistrement['_mots_cle'])) {
406
		    // ne dépend pas de cel_obs, et seront insérés *après* les enregistrements
459
		    // ne dépend pas de cel_obs, et seront insérés *après* les enregistrements
407
		    // mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
460
		    // mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
408
		    $tous_mots_cle[] = Array("mots_cle" => $last['_mots_cle'],
461
		    $tous_mots_cle[] = Array("mots_cle" => $last['_mots_cle'],
409
					     "obs_pos" => $pos);
462
					     "obs_pos" => $pos);
410
		    // la version inlinée des mots est enregistrées dans cel_obs
463
		    // la version inlinée des mots est enregistrées dans cel_obs
411
		    // mais cel_mots_cles_obs fait foi.
464
		    // mais cel_mots_cles_obs fait foi.
412
		    // XXX: postponer l'ajout de ces informations dans cel_obs *après* l'insertion effective
465
		    // XXX: postponer l'ajout de ces informations dans cel_obs *après* l'insertion effective
413
		    // des records dans cel_mots_cles_obs ?
466
		    // des records dans cel_mots_cles_obs ?
414
		    unset($last['_mots_cle']);
467
		    unset($last['_mots_cle']);
415
		}
468
		}
416
 
469
 
417
		$dernier_ordre++;
470
		$dernier_ordre++;
418
	    }
471
	    }
419
	}
472
	}
420
 
473
 
421
	// XXX future: return Array($enregistrements_a_inserer, $enregistrements_a_MAJ, $toutes_images);
474
	// XXX future: return Array($enregistrements_a_inserer, $enregistrements_a_MAJ, $toutes_images);
422
	return Array($enregistrements, $toutes_images, $tous_mots_cle);
475
	return Array($enregistrements, $toutes_images, $tous_mots_cle);
423
    }
476
    }
424
 
477
 
425
 
478
 
426
    static function trierColonnes(&$enregistrements) {
479
    static function trierColonnes(&$enregistrements) {
427
	foreach($enregistrements as &$enregistrement) {
480
	foreach($enregistrements as &$enregistrement) {
428
	    $enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
481
	    $enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
429
	    //array_walk($enregistrement, function(&$item, $k) { $item = is_null($item) ? "NULL" : $item; });
482
	    //array_walk($enregistrement, function(&$item, $k) { $item = is_null($item) ? "NULL" : $item; });
430
	    //$req .= implode(', ', $enregistrement) . "\n";
483
	    //$req .= implode(', ', $enregistrement) . "\n";
431
	}
484
	}
432
    }
485
    }
433
 
486
 
434
 
487
 
435
    static function stockerMotsCle($enregistrements, $tous_mots_cle, $lastid) {
488
    static function stockerMotsCle($enregistrements, $tous_mots_cle, $lastid) {
436
	$c = 0;
489
	$c = 0;
437
	// debug: var_dump($tous_mots_cle);die;
490
	// debug: var_dump($tous_mots_cle);die;
438
	foreach($tous_mots_cle as $v) $c += count($v['mots_cle']['to_insert']);
491
	foreach($tous_mots_cle as $v) $c += count($v['mots_cle']['to_insert']);
439
	return $c;
492
	return $c;
440
    }
493
    }
441
 
494
 
442
    static function stockerImages($enregistrements, $toutes_images, $lastid) {
495
    static function stockerImages($enregistrements, $toutes_images, $lastid) {
443
	$images_insert = 'INSERT INTO cel_obs_images (id_image, id_observation) VALUES %s ON DUPLICATE KEY UPDATE id_image = id_image';
496
	$images_insert = 'INSERT INTO cel_obs_images (id_image, id_observation) VALUES %s ON DUPLICATE KEY UPDATE id_image = id_image';
444
	$images_obs_assoc = Array();
497
	$images_obs_assoc = Array();
445
 
498
 
446
	foreach($toutes_images as $images_pour_obs) {
499
	foreach($toutes_images as $images_pour_obs) {
447
	    $obs = $enregistrements[$images_pour_obs["obs_pos"]];
500
	    $obs = $enregistrements[$images_pour_obs["obs_pos"]];
448
	    $id_obs = $lastid // dernier autoinc inséré
501
	    $id_obs = $lastid // dernier autoinc inséré
449
		- count($enregistrements) + 1 // correspondrait au premier autoinc
502
		- count($enregistrements) + 1 // correspondrait au premier autoinc
450
		+ $images_pour_obs["obs_pos"]; // ordre d'insertion = ordre dans le tableau $enregistrements (commence à 0)
503
		+ $images_pour_obs["obs_pos"]; // ordre d'insertion = ordre dans le tableau $enregistrements (commence à 0)
451
	    foreach($images_pour_obs['images'] as $image) {
504
	    foreach($images_pour_obs['images'] as $image) {
452
		$images_obs_assoc[] = sprintf('(%d,%d)',
505
		$images_obs_assoc[] = sprintf('(%d,%d)',
453
					      $image['id_image'], // intval() useless
506
					      $image['id_image'], // intval() useless
454
					      $id_obs); // intval() useless
507
					      $id_obs); // intval() useless
455
	    }
508
	    }
456
	}
509
	}
457
 
510
 
458
	if($images_obs_assoc) {
511
	if($images_obs_assoc) {
459
	    $requete = sprintf($images_insert, implode(', ', $images_obs_assoc));
512
	    $requete = sprintf($images_insert, implode(', ', $images_obs_assoc));
460
	    // debug echo "$requete\n";
513
	    // debug echo "$requete\n";
461
	    Cel::db()->requeter($requete);
514
	    Cel::db()->requeter($requete);
462
	}
515
	}
463
 
516
 
464
	return count($images_obs_assoc);
517
	return count($images_obs_assoc);
465
    }
518
    }
466
 
519
 
467
    /*
520
    /*
468
      Aucune des valeurs présentes dans $enregistrement n'est quotée
521
      Aucune des valeurs présentes dans $enregistrement n'est quotée
469
      cad aucune des valeurs retournée par traiter{Espece|Localisation}()
522
      cad aucune des valeurs retournée par traiter{Espece|Localisation}()
470
      car ce tableau est passé à un PDO::preparedStatement() qui applique
523
      car ce tableau est passé à un PDO::preparedStatement() qui applique
471
      proprement les règle d'échappement.
524
      proprement les règle d'échappement.
472
    */
525
    */
473
    static function chargerLigne($ligne, $dernier_ordre, $cel) {
526
    static function chargerLigne($ligne, $dernier_ordre, $cel) {
474
	// évite des notices d'index lors des trigger_error()
527
	// évite des notices d'index lors des trigger_error()
475
	$ref_ligne = !empty($ligne[C_NOM_SEL]) ? trim($ligne[C_NOM_SEL]) : '';
528
	$ref_ligne = !empty($ligne[C_NOM_SEL]) ? trim($ligne[C_NOM_SEL]) : '';
476
 
529
 
477
	// en premier car le résultat est utile pour
530
	// en premier car le résultat est utile pour
478
	// * traiter espèce (traiterEspece())
531
	// * traiter espèce (traiterEspece())
479
	// * traiter longitude et latitude (traiterLonLat())
532
	// * traiter longitude et latitude (traiterLonLat())
480
	$referentiel = self::identReferentiel(trim(strtolower(@$ligne[C_NOM_REFERENTIEL])), $ligne, $ref_ligne);
533
	$referentiel = self::identReferentiel(trim(strtolower(@$ligne[C_NOM_REFERENTIEL])), $ligne, $ref_ligne);
481
 
534
 
482
	// $espece est rempli de plusieurs informations
535
	// $espece est rempli de plusieurs informations
483
	$espece = Array(C_NOM_SEL => NULL, C_NOM_SEL_NN => NULL, C_NOM_RET => NULL,
536
	$espece = Array(C_NOM_SEL => NULL, C_NOM_SEL_NN => NULL, C_NOM_RET => NULL,
484
			C_NOM_RET_NN => NULL, C_NT => NULL, C_FAMILLE => NULL);
537
			C_NOM_RET_NN => NULL, C_NT => NULL, C_FAMILLE => NULL);
485
	self::traiterEspece($ligne, $espece, $referentiel, $cel->taxon_info_webservice);
538
	self::traiterEspece($ligne, $espece, $referentiel, $cel->taxon_info_webservice);
486
 
539
 
487
	if(!$espece[C_NOM_SEL]) $referentiel = Cel::$fallback_referentiel;
540
	if(!$espece[C_NOM_SEL]) $referentiel = Cel::$fallback_referentiel;
488
	if($espece[C_NOM_SEL] && !$espece[C_NOM_SEL_NN]) $referentiel = Cel::$fallback_referentiel;
541
	if($espece[C_NOM_SEL] && !$espece[C_NOM_SEL_NN]) $referentiel = Cel::$fallback_referentiel;
489
 
542
 
490
	// $localisation est rempli à partir de plusieurs champs: C_ZONE_GEO et C_CE_ZONE_GEO
543
	// $localisation est rempli à partir de plusieurs champs: C_ZONE_GEO et C_CE_ZONE_GEO
491
	$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
544
	$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
492
	self::traiterLocalisation($ligne, $localisation);
545
	self::traiterLocalisation($ligne, $localisation);
493
 
546
 
494
	// $transmission est utilisé pour date_transmission
547
	// $transmission est utilisé pour date_transmission
495
	// XXX: @ contre "Undefined index"
548
	// XXX: @ contre "Undefined index"
496
	@$transmission = in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0;
549
	@$transmission = in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0;
497
 
550
 
498
 
551
 
499
	// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
552
	// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
500
	// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
553
	// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
501
	$enregistrement = Array(
554
	$enregistrement = Array(
502
	    "ordre" => $dernier_ordre,
555
	    "ordre" => $dernier_ordre,
503
 
556
 
504
	    "nom_sel" => $espece[C_NOM_SEL],
557
	    "nom_sel" => $espece[C_NOM_SEL],
505
	    "nom_sel_nn" => $espece[C_NOM_SEL_NN],
558
	    "nom_sel_nn" => $espece[C_NOM_SEL_NN],
506
	    "nom_ret" => $espece[C_NOM_RET],
559
	    "nom_ret" => $espece[C_NOM_RET],
507
	    "nom_ret_nn" => $espece[C_NOM_RET_NN],
560
	    "nom_ret_nn" => $espece[C_NOM_RET_NN],
508
	    "nt" => $espece[C_NT],
561
	    "nt" => $espece[C_NT],
509
	    "famille" => $espece[C_FAMILLE],
562
	    "famille" => $espece[C_FAMILLE],
510
 
563
 
511
	    "nom_referentiel" => $referentiel,
564
	    "nom_referentiel" => $referentiel,
512
 
565
 
513
	    "zone_geo" => $localisation[C_ZONE_GEO],
566
	    "zone_geo" => $localisation[C_ZONE_GEO],
514
	    "ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
567
	    "ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
515
 
568
 
516
	    // $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
569
	    // $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
517
	    "date_observation" => isset($ligne[C_DATE_OBSERVATION]) ? self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ref_ligne) : NULL,
570
	    "date_observation" => isset($ligne[C_DATE_OBSERVATION]) ? self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ref_ligne) : NULL,
518
 
571
 
519
	    "lieudit" => isset($ligne[C_LIEUDIT]) ? trim($ligne[C_LIEUDIT]) : NULL,
572
	    "lieudit" => isset($ligne[C_LIEUDIT]) ? trim($ligne[C_LIEUDIT]) : NULL,
520
	    "station" => isset($ligne[C_STATION]) ? trim($ligne[C_STATION]) : NULL,
573
	    "station" => isset($ligne[C_STATION]) ? trim($ligne[C_STATION]) : NULL,
521
	    "milieu" => isset($ligne[C_MILIEU]) ? trim($ligne[C_MILIEU]) : NULL,
574
	    "milieu" => isset($ligne[C_MILIEU]) ? trim($ligne[C_MILIEU]) : NULL,
522
 
575
 
523
	    "mots_cles_texte" => NULL, // TODO: foreign-key
576
	    "mots_cles_texte" => NULL, // TODO: foreign-key
524
	    // XXX: @ contre "Undefined index"
577
	    // XXX: @ contre "Undefined index"
525
	    "commentaire" => isset($ligne[C_COMMENTAIRE]) ? trim($ligne[C_COMMENTAIRE]) : NULL,
578
	    "commentaire" => isset($ligne[C_COMMENTAIRE]) ? trim($ligne[C_COMMENTAIRE]) : NULL,
526
 
579
 
527
	    "transmission" => $transmission,
580
	    "transmission" => $transmission,
528
	    "date_transmission" => $transmission ? date("Y-m-d H:i:s") : NULL, // pas de fonction SQL dans un PDO statement, <=> now()
581
	    "date_transmission" => $transmission ? date("Y-m-d H:i:s") : NULL, // pas de fonction SQL dans un PDO statement, <=> now()
529
 
582
 
530
	    // $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
583
	    // $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
531
	    "latitude" => isset($ligne[C_LATITUDE]) ? self::traiterLonLat(NULL, $ligne[C_LATITUDE], $referentiel, $ref_ligne) : NULL,
584
	    "latitude" => isset($ligne[C_LATITUDE]) ? self::traiterLonLat(NULL, $ligne[C_LATITUDE], $referentiel, $ref_ligne) : NULL,
532
	    "longitude" => isset($ligne[C_LONGITUDE]) ? self::traiterLonLat($ligne[C_LONGITUDE], NULL, $referentiel, $ref_ligne) : NULL,
585
	    "longitude" => isset($ligne[C_LONGITUDE]) ? self::traiterLonLat($ligne[C_LONGITUDE], NULL, $referentiel, $ref_ligne) : NULL,
533
	    "altitude" => isset($ligne[C_ALTITUDE]) ? intval($ligne[C_ALTITUDE]) : NULL, // TODO: guess alt from lon/lat
586
	    "altitude" => isset($ligne[C_ALTITUDE]) ? intval($ligne[C_ALTITUDE]) : NULL, // TODO: guess alt from lon/lat
534
 
587
 
535
	    // @ car potentiellement optionnelles ou toutes vides => pas d'index dans PHPExcel (tableau optimisé)
588
	    // @ car potentiellement optionnelles ou toutes vides => pas d'index dans PHPExcel (tableau optimisé)
536
	    "abondance" => @$ligne[C_ABONDANCE],
589
	    "abondance" => @$ligne[C_ABONDANCE],
537
	    "certitude" => @$ligne[C_CERTITUDE],
590
	    "certitude" => @$ligne[C_CERTITUDE],
538
	    "phenologie" => @$ligne[C_PHENOLOGIE],
591
	    "phenologie" => @$ligne[C_PHENOLOGIE],
539
 
592
 
540
	    "code_insee_calcule" => substr($localisation[C_CE_ZONE_GEO], -5) // varchar(5)
593
	    "code_insee_calcule" => substr($localisation[C_CE_ZONE_GEO], -5) // varchar(5)
541
	);
594
	);
542
 
595
 
543
	// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
596
	// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
544
	// que si des résultats sont trouvés
597
	// que si des résultats sont trouvés
545
	// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
598
	// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
546
	if(@$ligne[C_IMAGES]) self::traiterImage($ligne[C_IMAGES], $cel->id_utilisateur, $enregistrement);
599
	if(@$ligne[C_IMAGES]) self::traiterImage($ligne[C_IMAGES], $cel->id_utilisateur, $enregistrement);
547
 
600
 
548
	if(@$ligne[C_MOTS_CLES_TEXTE]) self::traiterMotsCle($ligne[C_MOTS_CLES_TEXTE], $cel->id_utilisateur, $enregistrement);
601
	if(@$ligne[C_MOTS_CLES_TEXTE]) self::traiterMotsCle($ligne[C_MOTS_CLES_TEXTE], $cel->id_utilisateur, $enregistrement);
549
 
602
 
550
	return $enregistrement;
603
	return $enregistrement;
551
    }
604
    }
552
 
605
 
553
    static function traiterImage($str, $id_utilisateur, &$enregistrement) {
606
    static function traiterImage($str, $id_utilisateur, &$enregistrement) {
554
	$liste_images = array_filter(explode("/", $str));
607
	$liste_images = array_filter(explode("/", $str));
555
 
608
 
556
	//array_walk($liste_images, '__anonyme_4');
609
	//array_walk($liste_images, '__anonyme_4');
557
	array_walk($liste_images, array(__CLASS__, '__anonyme_4'));
610
	array_walk($liste_images, array(__CLASS__, '__anonyme_4'));
558
	$requete = sprintf(
611
	$requete = sprintf(
559
	    "SELECT id_image, nom_original FROM cel_images WHERE ce_utilisateur = %d AND nom_original IN (%s)",
612
	    "SELECT id_image, nom_original FROM cel_images WHERE ce_utilisateur = %d AND nom_original IN (%s)",
560
	    $id_utilisateur,
613
	    $id_utilisateur,
561
	    implode(',', $liste_images));
614
	    implode(',', $liste_images));
562
 
615
 
563
	$resultat = Cel::db()->requeter($requete);
616
	$resultat = Cel::db()->requeter($requete);
564
 
617
 
565
	if($resultat) $enregistrement['_images'] = $resultat;
618
	if($resultat) $enregistrement['_images'] = $resultat;
566
    }
619
    }
567
 
620
 
568
    static function traiterMotsCle($str, $id_utilisateur, &$enregistrement) {
621
    static function traiterMotsCle($str, $id_utilisateur, &$enregistrement) {
569
	$liste_mots_cle = $liste_mots_cle_recherche = array_map("trim", array_unique(array_filter(explode(",", $str))));
622
	$liste_mots_cle = $liste_mots_cle_recherche = array_map("trim", array_unique(array_filter(explode(",", $str))));
570
	array_walk($liste_mots_cle_recherche, array(__CLASS__, '__anonyme_4'));
623
	array_walk($liste_mots_cle_recherche, array(__CLASS__, '__anonyme_4'));
571
 
624
 
572
	// TODO!!!! remplace > (pour les tests uniquement) par un = et supprimer le group by mot_cle
625
	// TODO!!!! remplace > (pour les tests uniquement) par un = et supprimer le group by mot_cle
573
	$requete = sprintf("SELECT id_mot_cle_obs, mot_cle FROM cel_mots_cles_obs WHERE id_utilisateur > %d ".
626
	$requete = sprintf("SELECT id_mot_cle_obs, mot_cle FROM cel_mots_cles_obs WHERE id_utilisateur > %d ".
574
			   "AND mot_cle IN (%s) ".
627
			   "AND mot_cle IN (%s) ".
575
			   "GROUP BY mot_cle",
628
			   "GROUP BY mot_cle",
576
			   $id_utilisateur,
629
			   $id_utilisateur,
577
			   implode(',', $liste_mots_cle_recherche));
630
			   implode(',', $liste_mots_cle_recherche));
578
 
631
 
579
	$resultat_sql = Cel::db()->requeter($requete);
632
	$resultat_sql = Cel::db()->requeter($requete);
580
	if(!$resultat_sql) return;
633
	if(!$resultat_sql) return;
581
 
634
 
582
	$resultat = array();
635
	$resultat = array();
583
	foreach($resultat_sql as $v) $resultat[$v['id_mot_cle_obs']] = $v['mot_cle'];
636
	foreach($resultat_sql as $v) $resultat[$v['id_mot_cle_obs']] = $v['mot_cle'];
584
 
637
 
585
	$enregistrement['mots_cles_texte'] = implode(',', $liste_mots_cle);
638
	$enregistrement['mots_cles_texte'] = implode(',', $liste_mots_cle);
586
	$enregistrement['_mots_cle'] = array("existing" => $resultat,
639
	$enregistrement['_mots_cle'] = array("existing" => $resultat,
587
					     "to_insert" => array_diff($liste_mots_cle, $resultat));
640
					     "to_insert" => array_diff($liste_mots_cle, $resultat));
588
    }
641
    }
589
 
642
 
590
 
643
 
591
    /* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
644
    /* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
592
 
645
 
593
    // TODO: PHP 5.3, utiliser date_parse_from_format()
646
    // TODO: PHP 5.3, utiliser date_parse_from_format()
594
    // TODO: parser les heures (cf product-owner)
647
    // TODO: parser les heures (cf product-owner)
595
    // TODO: passer par le timestamp pour s'assurer de la validité
648
    // TODO: passer par le timestamp pour s'assurer de la validité
596
    static function traiterDateObs($date, $ref_ligne) {
649
    static function traiterDateObs($date, $ref_ligne) {
597
	// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
650
	// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
598
	// TODO: PHPExcel_Shared_Date::ExcelToPHP()
651
	// TODO: PHPExcel_Shared_Date::ExcelToPHP()
599
	if(is_double($date)) {
652
	if(is_double($date)) {
600
	    if($date > 0)
653
	    if($date > 0)
601
		return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
654
		return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
602
	    trigger_error("ligne \"{$ref_ligne}\": " .
655
	    trigger_error("ligne \"{$ref_ligne}\": " .
603
			  "Attention: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble",
656
			  "Attention: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble",
604
			  E_USER_NOTICE);
657
			  E_USER_NOTICE);
605
 
658
 
606
	    // throw new Exception("erreur: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble");
659
	    // throw new Exception("erreur: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble");
607
 
660
 
608
	    // attention, UNIX timestamp, car Excel les décompte depuis 1900
661
	    // attention, UNIX timestamp, car Excel les décompte depuis 1900
609
	    // cf http://fczaja.blogspot.fr/2011/06/convert-excel-date-into-timestamp.html
662
	    // cf http://fczaja.blogspot.fr/2011/06/convert-excel-date-into-timestamp.html
610
	    // $timestamp = ($date - MIN_DATES_DIFF) * 60 * 60 * 24 - time(); // NON
663
	    // $timestamp = ($date - MIN_DATES_DIFF) * 60 * 60 * 24 - time(); // NON
611
 
664
 
612
	    // $timestamp = PHPExcel_Calculation::getInstance()->calculateFormula("=" . $date . "-DATE(1970,1,1)*60*60*24"); // NON
665
	    // $timestamp = PHPExcel_Calculation::getInstance()->calculateFormula("=" . $date . "-DATE(1970,1,1)*60*60*24"); // NON
613
 
666
 
614
	    // echo strftime("%Y/%m/%d 00:00:00", $timestamp); // NON
667
	    // echo strftime("%Y/%m/%d 00:00:00", $timestamp); // NON
615
	}
668
	}
616
	else {
669
	else {
617
	    // attend l'un des formats de
670
	    // attend l'un des formats de
618
	    // http://www.php.net/manual/fr/datetime.formats.date.php
671
	    // http://www.php.net/manual/fr/datetime.formats.date.php
619
	    // le plus simple: YYYY/MM/DD (utilisé à l'export), mais DD-MM-YYYY est aussi supporté
672
	    // le plus simple: YYYY/MM/DD (utilisé à l'export), mais DD-MM-YYYY est aussi supporté
620
	    $matches = NULL;
673
	    $matches = NULL;
621
	    // et on essaie d'être sympa et supporter aussi DD/MM/YYYY
674
	    // et on essaie d'être sympa et supporter aussi DD/MM/YYYY
622
	    if(preg_match(';^([0-3]?\d)/([01]\d)/([12]\d\d\d)$;', $date, $matches)) {
675
	    if(preg_match(';^([0-3]?\d)/([01]\d)/([12]\d\d\d)$;', $date, $matches)) {
623
		$date = $matches[3] . '/' . $matches[2] . '/' . $matches[1];
676
		$date = $matches[3] . '/' . $matches[2] . '/' . $matches[1];
624
	    }
677
	    }
625
	    $timestamp = strtotime($date);
678
	    $timestamp = strtotime($date);
626
	    if(! $timestamp || $timestamp > time() + 3600 * 24 * 1) { // une journée d'avance maxi autorisée (décallage horaire ?)
679
	    if(! $timestamp || $timestamp > time() + 3600 * 24 * 1) { // une journée d'avance maxi autorisée (décallage horaire ?)
627
		if($date) trigger_error("ligne \"{$ref_ligne}\": Attention: date erronée ($date)", E_USER_NOTICE);
680
		if($date) trigger_error("ligne \"{$ref_ligne}\": Attention: date erronée ($date)", E_USER_NOTICE);
628
		return NULL;
681
		return NULL;
629
	    }
682
	    }
630
	    return strftime("%Y-%m-%d 00:00:00", $timestamp);
683
	    return strftime("%Y-%m-%d 00:00:00", $timestamp);
631
	}
684
	}
632
    }
685
    }
633
 
686
 
634
    static function identReferentiel($referentiel, $ligne, $ref_ligne) {
687
    static function identReferentiel($referentiel, $ligne, $ref_ligne) {
635
	// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
688
	// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
636
	if(strpos($referentiel, 'bdtfx') !== FALSE) return 'bdtfx'; //:v1.01';
689
	if(strpos($referentiel, 'bdtfx') !== FALSE) return 'bdtfx'; //:v1.01';
637
	if(strpos($referentiel, 'bdtxa') !== FALSE) return 'bdtxa'; //:v1.00';
690
	if(strpos($referentiel, 'bdtxa') !== FALSE) return 'bdtxa'; //:v1.00';
638
	//if(strpos($referentiel, 'bdnff') !== FALSE) return 'bdnff'; //:4.02';
691
	//if(strpos($referentiel, 'bdnff') !== FALSE) return 'bdnff'; //:4.02';
639
	if(strpos($referentiel, 'bdnff') !== FALSE) return 'bdtfx';
692
	if(strpos($referentiel, 'bdnff') !== FALSE) return 'bdtfx';
640
	if(strpos($referentiel, 'isfan') !== FALSE) return 'isfan'; //:v1.00';
693
	if(strpos($referentiel, 'isfan') !== FALSE) return 'isfan'; //:v1.00';
641
	if(strpos($referentiel, 'autre') !== FALSE) return 'autre';
694
	if(strpos($referentiel, 'autre') !== FALSE) return 'autre';
642
 
695
 
643
	if($referentiel && isset($ligne[C_NOM_SEL]) && $ligne[C_NOM_SEL]) {
696
	if($referentiel && isset($ligne[C_NOM_SEL]) && $ligne[C_NOM_SEL]) {
644
	    trigger_error("ligne \"{$ref_ligne}\": Attention: référentiel \"{$referentiel}\" inconnu", E_USER_NOTICE);
697
	    trigger_error("ligne \"{$ref_ligne}\": Attention: référentiel \"{$referentiel}\" inconnu", E_USER_NOTICE);
645
	    return 'autre';
698
	    return 'autre';
646
	}
699
	}
647
 
700
 
648
	// pas de référentiel ou pas de NOM_SEL: NULL
701
	// pas de référentiel ou pas de NOM_SEL: NULL
649
	return NULL;
702
	return NULL;
650
	/* TODO: cf story,
703
	/* TODO: cf story,
651
	   En cas de NULL faire une seconde passe de détection à partir du nom saisi
704
	   En cas de NULL faire une seconde passe de détection à partir du nom saisi
652
	   + accepter les n° de version */
705
	   + accepter les n° de version */
653
    }
706
    }
654
 
707
 
655
    static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx', $ref_ligne) {
708
    static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx', $ref_ligne) {
656
	// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
709
	// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
657
	if($lon && is_string($lon)) $lon = str_replace(',', '.', $lon);
710
	if($lon && is_string($lon)) $lon = str_replace(',', '.', $lon);
658
	if($lat && is_string($lat)) $lat = str_replace(',', '.', $lat);
711
	if($lat && is_string($lat)) $lat = str_replace(',', '.', $lat);
659
 
712
 
660
	// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
713
	// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
661
	// tout en uniformisant le format de séparateur des décimales (le ".")
714
	// tout en uniformisant le format de séparateur des décimales (le ".")
662
	if($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) return sprintf('%.5F', $lon);
715
	if($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) return sprintf('%.5F', $lon);
663
	if($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) return sprintf('%.5F', $lat);
716
	if($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) return sprintf('%.5F', $lat);
664
 
717
 
665
	if($lon || $lat) {
718
	if($lon || $lat) {
666
	    trigger_error("ligne \"{$ref_ligne}\": " .
719
	    trigger_error("ligne \"{$ref_ligne}\": " .
667
			  "Attention: longitude ou latitude erronée",
720
			  "Attention: longitude ou latitude erronée",
668
			  E_USER_NOTICE);
721
			  E_USER_NOTICE);
669
	}
722
	}
670
	return NULL;
723
	return NULL;
671
 
724
 
672
	/* limite france métropole si bdtfx ? ou bdtxa ? ...
725
	/* limite france métropole si bdtfx ? ou bdtxa ? ...
673
	   NON!
726
	   NON!
674
	   Un taxon d'un référentiel donné peut être théoriquement observé n'importe où sur le globe.
727
	   Un taxon d'un référentiel donné peut être théoriquement observé n'importe où sur le globe.
675
	   Il n'y a pas lieu d'effectuer des restriction ici.
728
	   Il n'y a pas lieu d'effectuer des restriction ici.
676
	   Cependant des erreurs fréquentes (0,0 ou lon/lat inversées) peuvent être détectés ici.
729
	   Cependant des erreurs fréquentes (0,0 ou lon/lat inversées) peuvent être détectés ici.
677
	   TODO */
730
	   TODO */
678
	$bbox = self::getReferentielBBox($referentiel);
731
	$bbox = self::getReferentielBBox($referentiel);
679
	if(!$bbox) return NULL;
732
	if(!$bbox) return NULL;
680
 
733
 
681
	if($lon) {
734
	if($lon) {
682
	    if($lon < $bbox['EST'] && $lon > $bbox['OUEST']) return is_numeric($lon) ? $lon : NULL;
735
	    if($lon < $bbox['EST'] && $lon > $bbox['OUEST']) return is_numeric($lon) ? $lon : NULL;
683
	    else return NULL;
736
	    else return NULL;
684
	}
737
	}
685
	if($lat) {
738
	if($lat) {
686
	    if($lat < $bbox['NORD'] && $lat > $bbox['SUD']) return is_numeric($lat) ? $lat : NULL;
739
	    if($lat < $bbox['NORD'] && $lat > $bbox['SUD']) return is_numeric($lat) ? $lat : NULL;
687
	    return NULL;
740
	    return NULL;
688
	}
741
	}
689
    }
742
    }
690
 
743
 
691
    /*
744
    /*
692
      TODO: s'affranchir du webservice pour la détermination du nom scientifique en s'appuyant sur cel_references,
745
      TODO: s'affranchir du webservice pour la détermination du nom scientifique en s'appuyant sur cel_references,
693
      pour des questions de performances
746
      pour des questions de performances
694
    */
747
    */
695
    static function traiterEspece($ligne, Array &$espece, &$referentiel, $taxon_info_webservice) {
748
    static function traiterEspece($ligne, Array &$espece, &$referentiel, $taxon_info_webservice) {
696
	if(empty($ligne[C_NOM_SEL])) {
749
	if(empty($ligne[C_NOM_SEL])) {
697
	    // TODO: nous ne déclarons pas "Numéro nomenclatural" comme colonne importable
750
	    // TODO: nous ne déclarons pas "Numéro nomenclatural" comme colonne importable
698
	    // Nous ne pouvons donc pas tenter d'être sympa sur la détermination par num_nom
751
	    // Nous ne pouvons donc pas tenter d'être sympa sur la détermination par num_nom
699
	    /* if(!empty($ligne[C_NOM_SEL_NN]) && $referentiel != Cel::$fallback_referentiel)
752
	    /* if(!empty($ligne[C_NOM_SEL_NN]) && $referentiel != Cel::$fallback_referentiel)
700
	       $ligne[C_NOM_SEL] = $referentiel . ':nn:' . $ligne[C_NOM_SEL_NN];
753
	       $ligne[C_NOM_SEL] = $referentiel . ':nn:' . $ligne[C_NOM_SEL_NN];
701
	       else */
754
	       else */
702
	    return;
755
	    return;
703
	}
756
	}
704
 
757
 
705
	// nom_sel reste toujours celui de l'utilisateur
758
	// nom_sel reste toujours celui de l'utilisateur
706
	$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
759
	$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
707
 
760
 
708
	// XXX/attention, nous ne devrions pas accepter un référentiel absent !
761
	// XXX/attention, nous ne devrions pas accepter un référentiel absent !
709
	if(!$referentiel) $referentiel = 'bdtfx';
762
	if(!$referentiel) $referentiel = 'bdtfx';
710
	$taxon_info_webservice->setReferentiel($referentiel);
763
	$taxon_info_webservice->setReferentiel($referentiel);
711
	$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
764
	$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
712
 
765
 
713
	// TODO: si empty(C_NOM_SEL) et !empty(C_NOM_SEL_NN) : recherche info à partir de C_NOM_SEL_NN
766
	// TODO: si empty(C_NOM_SEL) et !empty(C_NOM_SEL_NN) : recherche info à partir de C_NOM_SEL_NN
714
	// echo "rechercherInformationsComplementairesSurNom()\n";
767
	// echo "rechercherInformationsComplementairesSurNom()\n";
715
	/*
768
	/*
716
	  SELECT num_nom, nom_sci, num_nom_retenu ,auteur, annee, biblio_origine, nom_sci,auteur  FROM bdtfx_v1_01  WHERE (nom_sci LIKE 'Heliotropium europaeum')  ORDER BY nom_sci ASC	  LIMIT 0, 1
769
	  SELECT num_nom, nom_sci, num_nom_retenu ,auteur, annee, biblio_origine, nom_sci,auteur  FROM bdtfx_v1_01  WHERE (nom_sci LIKE 'Heliotropium europaeum')  ORDER BY nom_sci ASC	  LIMIT 0, 1
717
	  #
770
	  #
718
	  SELECT num_nom, nom_sci, num_nom_retenu ,auteur, annee, biblio_origine, nom_sci,auteur  FROM bdtfx_v1_01  WHERE (nom_sci LIKE 'eliotropium euro')  ORDER BY nom_sci ASC   LIMIT 0, 1
771
	  SELECT num_nom, nom_sci, num_nom_retenu ,auteur, annee, biblio_origine, nom_sci,auteur  FROM bdtfx_v1_01  WHERE (nom_sci LIKE 'eliotropium euro')  ORDER BY nom_sci ASC   LIMIT 0, 1
719
	  SELECT num_nom, nom_sci, num_nom_retenu ,auteur, annee, biblio_origine, nom_sci,auteur  FROM bdtfx_v1_01  WHERE (nom_sci LIKE 'eliotropium')	ORDER BY nom_sci ASC   LIMIT 0, 1
772
	  SELECT num_nom, nom_sci, num_nom_retenu ,auteur, annee, biblio_origine, nom_sci,auteur  FROM bdtfx_v1_01  WHERE (nom_sci LIKE 'eliotropium')	ORDER BY nom_sci ASC   LIMIT 0, 1
720
	  SELECT num_nom, nom_sci, num_nom_retenu ,auteur, annee, biblio_origine, nom_sci,auteur  FROM bdtfx_v1_01  WHERE (nom_sci LIKE 'eliotropium% euro%')  ORDER BY nom_sci ASC   LIMIT 0, 1
773
	  SELECT num_nom, nom_sci, num_nom_retenu ,auteur, annee, biblio_origine, nom_sci,auteur  FROM bdtfx_v1_01  WHERE (nom_sci LIKE 'eliotropium% euro%')  ORDER BY nom_sci ASC   LIMIT 0, 1
721
	  #
774
	  #
722
 
775
 
723
	  SELECT nom_sci, num_nom_retenu, nom_sci_html, auteur, annee, biblio_origine FROM bdtfx_v1_01 WHERE num_nom = 31468
776
	  SELECT nom_sci, num_nom_retenu, nom_sci_html, auteur, annee, biblio_origine FROM bdtfx_v1_01 WHERE num_nom = 31468
724
	*/
777
	*/
725
	// $determ = $taxon_info_webservice->rechercherInformationsComplementairesSurNom($ligne[C_NOM_SEL]);
778
	// $determ = $taxon_info_webservice->rechercherInformationsComplementairesSurNom($ligne[C_NOM_SEL]);
726
	// permet une reconnaissance de bdtfx:nn:XXXX
779
	// permet une reconnaissance de bdtfx:nn:XXXX
727
	$determ = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax(trim($ligne[C_NOM_SEL]));
780
	$determ = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax(trim($ligne[C_NOM_SEL]));
728
 
781
 
729
	// note: rechercherInfosSurTexteCodeOuNumTax peut ne retourner qu'une seule clef "nom_sel"
782
	// note: rechercherInfosSurTexteCodeOuNumTax peut ne retourner qu'une seule clef "nom_sel"
730
	if (! $determ) {
783
	if (! $determ) {
731
	    // on supprime les noms retenus et renvoi tel quel
784
	    // on supprime les noms retenus et renvoi tel quel
732
	    // on réutilise les define pour les noms d'indexes, tant qu'à faire
785
	    // on réutilise les define pour les noms d'indexes, tant qu'à faire
733
	    // XXX; tout à NULL sauf C_NOM_SEL ci-dessus ?
786
	    // XXX; tout à NULL sauf C_NOM_SEL ci-dessus ?
734
	    $espece[C_NOM_SEL_NN] = @$ligne[C_NOM_SEL_NN];
787
	    $espece[C_NOM_SEL_NN] = @$ligne[C_NOM_SEL_NN];
735
	    $espece[C_NOM_RET] = @$ligne[C_NOM_RET];
788
	    $espece[C_NOM_RET] = @$ligne[C_NOM_RET];
736
	    $espece[C_NOM_RET_NN] = @$ligne[C_NOM_RET_NN];
789
	    $espece[C_NOM_RET_NN] = @$ligne[C_NOM_RET_NN];
737
	    $espece[C_NT] = @$ligne[C_NT];
790
	    $espece[C_NT] = @$ligne[C_NT];
738
	    $espece[C_FAMILLE] = @$ligne[C_FAMILLE];
791
	    $espece[C_FAMILLE] = @$ligne[C_FAMILLE];
739
 
792
 
740
	    return;
793
	    return;
741
	}
794
	}
742
 
795
 
743
	// succès de la détection, mais résultat partiel
796
	// succès de la détection, mais résultat partiel
744
	if(!isset($determ->id)) 
797
	if(!isset($determ->id)) 
745
	    $determ = $taxon_info_webservice->effectuerRequeteInfosComplementairesSurNumNom($determ->{"nom_retenu.id"});
798
	    $determ = $taxon_info_webservice->effectuerRequeteInfosComplementairesSurNumNom($determ->{"nom_retenu.id"});
746
 
799
 
747
	// ne devrait jamais arriver !
800
	// ne devrait jamais arriver !
748
	if(!$determ) die("erreur critique: " . __FILE__ . ':' . __LINE__);
801
	if(!$determ) die("erreur critique: " . __FILE__ . ':' . __LINE__);
749
 
802
 
750
	// un schéma <ref>:(nt|nn):<num> (ie: bdtfx:nt:8503) a été passé
803
	// un schéma <ref>:(nt|nn):<num> (ie: bdtfx:nt:8503) a été passé
751
	// dans ce cas on met à jour le référentiel avec celui passé dans le champ espèce
804
	// dans ce cas on met à jour le référentiel avec celui passé dans le champ espèce
752
	if(isset($determ->ref)) {
805
	if(isset($determ->ref)) {
753
	    $referentiel = $determ->ref;
806
	    $referentiel = $determ->ref;
754
	}
807
	}
755
 
808
 
756
	// succès de la détection
809
	// succès de la détection
757
	// nom_sel est remplacé, mais seulement si un motif spécial à été utilisé (bdtfx:nn:4567)
810
	// nom_sel est remplacé, mais seulement si un motif spécial à été utilisé (bdtfx:nn:4567)
758
	if($taxon_info_webservice->is_notation_spe) {
811
	if($taxon_info_webservice->is_notation_spe) {
759
	    $espece[C_NOM_SEL] = $determ->nom_sci;
812
	    $espece[C_NOM_SEL] = $determ->nom_sci;
760
	}
813
	}
761
 
814
 
762
	// écrasement des numéros (nomenclatural, taxonomique) saisis...
815
	// écrasement des numéros (nomenclatural, taxonomique) saisis...
763
	$espece[C_NOM_SEL_NN] = $determ->id;
816
	$espece[C_NOM_SEL_NN] = $determ->id;
764
	$espece[C_NOM_RET] = RechercheInfosTaxonBeta::supprimerBiblio($determ->nom_retenu_complet);
817
	$espece[C_NOM_RET] = RechercheInfosTaxonBeta::supprimerBiblio($determ->nom_retenu_complet);
765
	$espece[C_NOM_RET_NN] = $determ->{"nom_retenu.id"};
818
	$espece[C_NOM_RET_NN] = $determ->{"nom_retenu.id"};
766
	$espece[C_NT] = $determ->num_taxonomique;
819
	$espece[C_NT] = $determ->num_taxonomique;
767
	$espece[C_FAMILLE] = $determ->famille;
820
	$espece[C_FAMILLE] = $determ->famille;
768
	return;
821
	return;
769
	// et des info complémentaires
822
	// et des info complémentaires
770
 
823
 
771
	/*
824
	/*
772
	// GET /service:eflore:0.1/bdtfx/noms/31468?retour.champs=nom_sci,auteur,id,nom_retenu_complet,nom_retenu.id,num_taxonomique,famille
825
	// GET /service:eflore:0.1/bdtfx/noms/31468?retour.champs=nom_sci,auteur,id,nom_retenu_complet,nom_retenu.id,num_taxonomique,famille
773
	/home/raphael/eflore/projets/services/modules/0.1/bdtfx/Noms.php:280
826
	/home/raphael/eflore/projets/services/modules/0.1/bdtfx/Noms.php:280
774
	SELECT  *, nom_sci   FROM bdtfx_v1_01	 WHERE num_nom = '31468' 
827
	SELECT  *, nom_sci   FROM bdtfx_v1_01	 WHERE num_nom = '31468' 
775
	SELECT nom_sci, num_nom_retenu, nom_sci_html, auteur, annee, biblio_origine FROM bdtfx_v1_01 WHERE num_nom = 31468
828
	SELECT nom_sci, num_nom_retenu, nom_sci_html, auteur, annee, biblio_origine FROM bdtfx_v1_01 WHERE num_nom = 31468
776
	SELECT nom_sci, num_nom_retenu, nom_sci_html, auteur, annee, biblio_origine FROM bdtfx_v1_01 WHERE num_nom = 86535
829
	SELECT nom_sci, num_nom_retenu, nom_sci_html, auteur, annee, biblio_origine FROM bdtfx_v1_01 WHERE num_nom = 86535
777
	*/
830
	*/
778
 
831
 
779
 
832
 
780
	//var_dump($complement, $espece);die;
833
	//var_dump($complement, $espece);die;
781
    }
834
    }
782
 
835
 
783
    static function detectFromNom($nom) {
836
    static function detectFromNom($nom) {
784
	$r = Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s') ".
837
	$r = Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s') ".
785
					 "ORDER BY nom_sci ASC LIMIT 0, 1",
838
					 "ORDER BY nom_sci ASC LIMIT 0, 1",
786
					 Cel::db()->proteger($nom)));
839
					 Cel::db()->proteger($nom)));
787
	if($r) return $r;
840
	if($r) return $r;
788
 
841
 
789
	Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s' OR nom LIKE '%s') ".
842
	Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s' OR nom LIKE '%s') ".
790
				    "ORDER BY nom_sci ASC LIMIT 0, 1",
843
				    "ORDER BY nom_sci ASC LIMIT 0, 1",
791
				    Cel::db()->proteger($nom),
844
				    Cel::db()->proteger($nom),
792
				    Cel::db()->proteger(str_replace(' ', '% ', $nom))));
845
				    Cel::db()->proteger(str_replace(' ', '% ', $nom))));
793
	return $r;
846
	return $r;
794
    }
847
    }
795
 
848
 
796
 
849
 
797
    /*
850
    /*
798
     * TODO: analyse rigoureuse:
851
     * TODO: analyse rigoureuse:
799
     * == Identifiant Commune
852
     * == Identifiant Commune
800
     * - INSEE-C:\d{5}
853
     * - INSEE-C:\d{5}
801
     * - \d{5}
854
     * - \d{5}
802
     * - \d{2}
855
     * - \d{2}
803
     * == Commune
856
     * == Commune
804
     * - \w+ (\d{2})
857
     * - \w+ (\d{2})
805
     * - \w+ (\d{5})
858
     * - \w+ (\d{5})
806
     * - \w+
859
     * - \w+
807
     *
860
     *
808
     */
861
     */
809
    static function traiterLocalisation($ligne, Array &$localisation) {
862
    static function traiterLocalisation($ligne, Array &$localisation) {
810
	if(empty($ligne[C_ZONE_GEO])) $ligne[C_ZONE_GEO] = NULL;
863
	if(empty($ligne[C_ZONE_GEO])) $ligne[C_ZONE_GEO] = NULL;
811
	if(empty($ligne[C_CE_ZONE_GEO])) $ligne[C_CE_ZONE_GEO] = NULL;
864
	if(empty($ligne[C_CE_ZONE_GEO])) $ligne[C_CE_ZONE_GEO] = NULL;
812
 
865
 
813
	$identifiant_commune = trim($ligne[C_ZONE_GEO]);
866
	$identifiant_commune = trim($ligne[C_ZONE_GEO]);
814
	if(!$identifiant_commune) {
867
	if(!$identifiant_commune) {
815
	    $departement = trim($ligne[C_CE_ZONE_GEO]);
868
	    $departement = trim($ligne[C_CE_ZONE_GEO]);
816
 
869
 
817
	    if(strpos($departement, "INSEE-C:", 0) === 0) {
870
	    if(strpos($departement, "INSEE-C:", 0) === 0) {
818
		$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
871
		$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
819
		if(array_key_exists($localisation[C_CE_ZONE_GEO], self::$cache['geo'])) {
872
		if(array_key_exists($localisation[C_CE_ZONE_GEO], self::$cache['geo'])) {
820
		    $localisation[C_ZONE_GEO] = self::$cache['geo'][$localisation[C_CE_ZONE_GEO]];
873
		    $localisation[C_ZONE_GEO] = self::$cache['geo'][$localisation[C_CE_ZONE_GEO]];
821
		}
874
		}
822
		else {
875
		else {
823
		    $nom = Cel::db()->requeter(sprintf("SELECT nom FROM cel_zones_geo WHERE code = %s LIMIT 1",
876
		    $nom = Cel::db()->requeter(sprintf("SELECT nom FROM cel_zones_geo WHERE code = %s LIMIT 1",
824
						       self::quoteNonNull(substr($localisation[C_CE_ZONE_GEO], strlen("INSEE-C:")))));
877
						       self::quoteNonNull(substr($localisation[C_CE_ZONE_GEO], strlen("INSEE-C:")))));
825
		    if($nom) $localisation[C_ZONE_GEO] = $nom[0]['nom'];
878
		    if($nom) $localisation[C_ZONE_GEO] = $nom[0]['nom'];
826
		    self::$cache['geo'][$localisation[C_CE_ZONE_GEO]] = @$nom[0]['nom'];
879
		    self::$cache['geo'][$localisation[C_CE_ZONE_GEO]] = @$nom[0]['nom'];
827
		}
880
		}
828
		return;
881
		return;
829
	    }
882
	    }
830
 
883
 
831
	    if(!is_numeric($departement)) {
884
	    if(!is_numeric($departement)) {
832
		$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
885
		$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
833
		return;
886
		return;
834
	    }
887
	    }
835
 
888
 
836
	    $cache_attempted = FALSE;
889
	    $cache_attempted = FALSE;
837
	    if(array_key_exists($departement, self::$cache['geo'])) {
890
	    if(array_key_exists($departement, self::$cache['geo'])) {
838
		$cache_attempted = TRUE;
891
		$cache_attempted = TRUE;
839
		if(self::$cache['geo'][$departement][0] && self::$cache['geo'][$departement][1]) {
892
		if(self::$cache['geo'][$departement][0] && self::$cache['geo'][$departement][1]) {
840
		    $localisation[C_ZONE_GEO] = self::$cache['geo'][$departement][0];
893
		    $localisation[C_ZONE_GEO] = self::$cache['geo'][$departement][0];
841
		    $localisation[C_CE_ZONE_GEO] = self::$cache['geo'][$departement][1];
894
		    $localisation[C_CE_ZONE_GEO] = self::$cache['geo'][$departement][1];
842
		    return;
895
		    return;
843
		}
896
		}
844
	    }
897
	    }
845
 
898
 
846
	    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",
899
	    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",
847
										      self::quoteNonNull($departement)))) ) {
900
										      self::quoteNonNull($departement)))) ) {
848
		$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
901
		$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
849
		$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
902
		$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
850
		self::$cache['geo'][$departement] = array($resultat_commune[0]['nom'], $resultat_commune[0]['code']);
903
		self::$cache['geo'][$departement] = array($resultat_commune[0]['nom'], $resultat_commune[0]['code']);
851
		return;
904
		return;
852
	    }
905
	    }
853
	    ;
906
	    ;
854
	    // if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
907
	    // if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
855
	    // if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
908
	    // if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
856
	    // if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
909
	    // if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
857
 
910
 
858
	    $departement = trim($departement); // TODO
911
	    $departement = trim($departement); // TODO
859
 
912
 
860
	    $localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
913
	    $localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
861
	    return;
914
	    return;
862
	}
915
	}
863
 
916
 
864
 
917
 
865
	$select = "SELECT DISTINCT nom, code FROM cel_zones_geo";
918
	$select = "SELECT DISTINCT nom, code FROM cel_zones_geo";
866
	
919
	
867
	if (preg_match('/(.+) \((\d{1,5})\)/', $identifiant_commune, $elements)) {
920
	if (preg_match('/(.+) \((\d{1,5})\)/', $identifiant_commune, $elements)) {
868
	    // commune + departement : montpellier (34)
921
	    // commune + departement : montpellier (34)
869
	    $nom_commune=$elements[1];
922
	    $nom_commune=$elements[1];
870
	    $code_commune=$elements[2];
923
	    $code_commune=$elements[2];
871
	    if(strlen($code_commune) <= 2) {
924
	    if(strlen($code_commune) <= 2) {
872
		$requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
925
		$requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
873
				   $select, self::quoteNonNull($nom_commune),
926
				   $select, self::quoteNonNull($nom_commune),
874
				   self::quoteNonNull($code_commune.'%'));
927
				   self::quoteNonNull($code_commune.'%'));
875
	    }
928
	    }
876
	    else {
929
	    else {
877
		$requete = sprintf("%s WHERE nom = %s AND code = %d",
930
		$requete = sprintf("%s WHERE nom = %s AND code = %d",
878
				   $select, self::quoteNonNull($nom_commune),
931
				   $select, self::quoteNonNull($nom_commune),
879
				   $code_commune);
932
				   $code_commune);
880
	    }
933
	    }
881
	}
934
	}
882
	elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
935
	elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
883
	    // Code insee seul  
936
	    // Code insee seul  
884
	    $code_insee_commune=$elements[1];
937
	    $code_insee_commune=$elements[1];
885
	    $requete = sprintf("%s WHERE code = %s", $select, self::quoteNonNull($code_insee_commune));
938
	    $requete = sprintf("%s WHERE code = %s", $select, self::quoteNonNull($code_insee_commune));
886
	}
939
	}
887
	else {
940
	else {
888
	    // Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
941
	    // Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
889
	    // on prend le risque ici de retourner une mauvaise Commune
942
	    // on prend le risque ici de retourner une mauvaise Commune
890
	    $nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
943
	    $nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
891
	    $requete = sprintf("%s WHERE nom LIKE %s", $select, self::quoteNonNull($nom_commune.'%'));
944
	    $requete = sprintf("%s WHERE nom LIKE %s", $select, self::quoteNonNull($nom_commune.'%'));
892
	}
945
	}
893
 
946
 
894
 
947
 
895
	if(array_key_exists($identifiant_commune, self::$cache['geo'])) {
948
	if(array_key_exists($identifiant_commune, self::$cache['geo'])) {
896
	    $resultat_commune = self::$cache['geo'][$identifiant_commune];
949
	    $resultat_commune = self::$cache['geo'][$identifiant_commune];
897
	}
950
	}
898
	else {
951
	else {
899
	    $resultat_commune = Cel::db()->requeter($requete);
952
	    $resultat_commune = Cel::db()->requeter($requete);
900
	    self::$cache['geo'][$identifiant_commune] = $resultat_commune;
953
	    self::$cache['geo'][$identifiant_commune] = $resultat_commune;
901
	}
954
	}
902
	// TODO: levenstein sort ?
955
	// TODO: levenstein sort ?
903
	// TODO: count résultat !
956
	// TODO: count résultat !
904
 
957
 
905
	// cas de la commune introuvable dans le référentiel
958
	// cas de la commune introuvable dans le référentiel
906
	// réinitialisation aux valeurs du fichier XLS
959
	// réinitialisation aux valeurs du fichier XLS
907
	if(! $resultat_commune) {
960
	if(! $resultat_commune) {
908
	    $localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
961
	    $localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
909
	    $localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
962
	    $localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
910
	} else {
963
	} else {
911
	    $localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
964
	    $localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
912
	    $localisation[C_CE_ZONE_GEO] = "INSEE-C:" . $resultat_commune[0]['code'];
965
	    $localisation[C_CE_ZONE_GEO] = "INSEE-C:" . $resultat_commune[0]['code'];
913
	    return;
966
	    return;
914
	}
967
	}
915
 
968
 
916
	$departement = &$localisation[C_CE_ZONE_GEO];
969
	$departement = &$localisation[C_CE_ZONE_GEO];
917
 
970
 
918
	if(strpos($departement, "INSEE-C:", 0) === 0) {
971
	if(strpos($departement, "INSEE-C:", 0) === 0) {
919
	    $localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
972
	    $localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
920
	    $localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
973
	    $localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
921
	}
974
	}
922
 
975
 
923
 
976
 
924
	if(!is_numeric($departement)) {
977
	if(!is_numeric($departement)) {
925
	    $localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
978
	    $localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
926
	    $localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
979
	    $localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
927
	}
980
	}
928
 
981
 
929
	if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
982
	if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
930
	if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
983
	if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
931
	// if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
984
	// if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
932
 
985
 
933
	$departement = trim($departement); // TODO
986
	$departement = trim($departement); // TODO
934
 
987
 
935
	$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
988
	$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
936
	$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
989
	$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
937
    }
990
    }
938
 
991
 
939
    /*
992
    /*
940
      static function traiterLocalisation($ligne, Array &$localisation) {
993
      static function traiterLocalisation($ligne, Array &$localisation) {
941
      $identifiant_commune = trim($ligne[C_ZONE_GEO]);
994
      $identifiant_commune = trim($ligne[C_ZONE_GEO]);
942
      if(!$identifiant_commune) {
995
      if(!$identifiant_commune) {
943
      $departement = trim($ligne[C_CE_ZONE_GEO]);
996
      $departement = trim($ligne[C_CE_ZONE_GEO]);
944
      goto testdepartement;
997
      goto testdepartement;
945
      }
998
      }
946
 
999
 
947
 
1000
 
948
      $select = "SELECT DISTINCT nom, code  FROM cel_zones_geo";
1001
      $select = "SELECT DISTINCT nom, code  FROM cel_zones_geo";
949
	
1002
	
950
      if (preg_match('/(.*) \((\d+)\)/', $identifiant_commune, $elements)) {
1003
      if (preg_match('/(.*) \((\d+)\)/', $identifiant_commune, $elements)) {
951
      // commune + departement : montpellier (34)
1004
      // commune + departement : montpellier (34)
952
      $nom_commune=$elements[1];
1005
      $nom_commune=$elements[1];
953
      $code_commune=$elements[2];
1006
      $code_commune=$elements[2];
954
      $requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
1007
      $requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
955
      $select, self::quoteNonNull($nom_commune), self::quoteNonNull($code_commune.'%'));
1008
      $select, self::quoteNonNull($nom_commune), self::quoteNonNull($code_commune.'%'));
956
      }
1009
      }
957
      elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
1010
      elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
958
      // Code insee seul  
1011
      // Code insee seul  
959
      $code_insee_commune=$elements[1];
1012
      $code_insee_commune=$elements[1];
960
      $requete = sprintf("%s WHERE code = %s", $select, self::quoteNonNull($code_insee_commune));
1013
      $requete = sprintf("%s WHERE code = %s", $select, self::quoteNonNull($code_insee_commune));
961
      }
1014
      }
962
      else {
1015
      else {
963
      // Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
1016
      // Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
964
      // on prend le risque ici de retourner une mauvaise Commune
1017
      // on prend le risque ici de retourner une mauvaise Commune
965
      $nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
1018
      $nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
966
      $requete = sprintf("%s WHERE nom LIKE %s", $select, self::quoteNonNull($nom_commune.'%'));
1019
      $requete = sprintf("%s WHERE nom LIKE %s", $select, self::quoteNonNull($nom_commune.'%'));
967
      }
1020
      }
968
	
1021
	
969
      $resultat_commune = Cel::db()->requeter($requete);
1022
      $resultat_commune = Cel::db()->requeter($requete);
970
      // TODO: levenstein sort ?
1023
      // TODO: levenstein sort ?
971
 
1024
 
972
      // cas de la commune introuvable dans le référentiel
1025
      // cas de la commune introuvable dans le référentiel
973
      // réinitialisation aux valeurs du fichier XLS
1026
      // réinitialisation aux valeurs du fichier XLS
974
      if(! $resultat_commune) {
1027
      if(! $resultat_commune) {
975
      $localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
1028
      $localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
976
      $localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
1029
      $localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
977
      } else {
1030
      } else {
978
      $localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
1031
      $localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
979
      $localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
1032
      $localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
980
      }
1033
      }
981
 
1034
 
982
      $departement = &$localisation[C_CE_ZONE_GEO];
1035
      $departement = &$localisation[C_CE_ZONE_GEO];
983
 
1036
 
984
      testdepartement:
1037
      testdepartement:
985
      if(strpos($departement, "INSEE-C:", 0) === 0) goto protectloc;
1038
      if(strpos($departement, "INSEE-C:", 0) === 0) goto protectloc;
986
 
1039
 
987
      if(!is_numeric($departement)) goto protectloc; // TODO ?
1040
      if(!is_numeric($departement)) goto protectloc; // TODO ?
988
      if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
1041
      if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
989
      if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
1042
      if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
990
      // if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
1043
      // if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
991
 
1044
 
992
      $departement = trim($departement); // TODO
1045
      $departement = trim($departement); // TODO
993
 
1046
 
994
      protectloc:
1047
      protectloc:
995
      $localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
1048
      $localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
996
      $localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
1049
      $localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
997
      }
1050
      }
998
    */
1051
    */
999
 
1052
 
1000
 
1053
 
1001
    /* HELPERS */
1054
    /* HELPERS */
1002
 
1055
 
1003
    // http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
1056
    // http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
1004
    // XXX; utilisé aussi (temporairement ?) par FormateurGroupeColonne.
1057
    // XXX; utilisé aussi (temporairement ?) par FormateurGroupeColonne.
1005
    static function sortArrayByArray($array, $orderArray) {
1058
    static function sortArrayByArray($array, $orderArray) {
1006
	$ordered = array();
1059
	$ordered = array();
1007
	foreach($orderArray as $key) {
1060
	foreach($orderArray as $key) {
1008
	    if(array_key_exists($key, $array)) {
1061
	    if(array_key_exists($key, $array)) {
1009
		$ordered[$key] = $array[$key];
1062
		$ordered[$key] = $array[$key];
1010
		unset($array[$key]);
1063
		unset($array[$key]);
1011
	    }
1064
	    }
1012
	}
1065
	}
1013
	return $ordered + $array;
1066
	return $ordered + $array;
1014
    }
1067
    }
1015
 
1068
 
1016
    // retourne une BBox [N,S,E,O) pour un référentiel donné
1069
    // retourne une BBox [N,S,E,O) pour un référentiel donné
1017
    static function getReferentielBBox($referentiel) {
1070
    static function getReferentielBBox($referentiel) {
1018
	if($referentiel == 'bdtfx') return Array(
1071
	if($referentiel == 'bdtfx') return Array(
1019
	    'NORD' => 51.2, // Dunkerque
1072
	    'NORD' => 51.2, // Dunkerque
1020
	    'SUD' => 41.3, // Bonifacio
1073
	    'SUD' => 41.3, // Bonifacio
1021
	    'EST' => 9.7, // Corse
1074
	    'EST' => 9.7, // Corse
1022
	    'OUEST' => -5.2); // Ouessan
1075
	    'OUEST' => -5.2); // Ouessan
1023
	return FALSE;
1076
	return FALSE;
1024
    }
1077
    }
1025
 
1078
 
1026
    // ces valeurs ne sont pas inséré via les placeholders du PDO::preparedStatement
1079
    // ces valeurs ne sont pas inséré via les placeholders du PDO::preparedStatement
1027
    // et doivent donc être échappées correctement.
1080
    // et doivent donc être échappées correctement.
1028
    public function initialiser_colonnes_statiques() {
1081
    public function initialiser_colonnes_statiques() {
1029
	$this->colonnes_statiques = array_merge($this->colonnes_statiques,
1082
	$this->colonnes_statiques = array_merge($this->colonnes_statiques,
1030
						Array(
1083
						Array(
1031
						    "ce_utilisateur" => self::quoteNonNull($this->id_utilisateur), // peut-être un hash ou un id
1084
						    "ce_utilisateur" => self::quoteNonNull($this->id_utilisateur), // peut-être un hash ou un id
1032
						    "prenom_utilisateur" => self::quoteNonNull($this->utilisateur['prenom']),
1085
						    "prenom_utilisateur" => self::quoteNonNull($this->utilisateur['prenom']),
1033
						    "nom_utilisateur" => self::quoteNonNull($this->utilisateur['nom']),
1086
						    "nom_utilisateur" => self::quoteNonNull($this->utilisateur['nom']),
1034
						    "courriel_utilisateur" => self::quoteNonNull($this->utilisateur['courriel']),
1087
						    "courriel_utilisateur" => self::quoteNonNull($this->utilisateur['courriel']),
1035
						));
1088
						));
1036
 
1089
 
1037
    }
1090
    }
1038
 
1091
 
1039
    static function initialiser_pdo_ordered_statements($colonnes_statiques) {
1092
    static function initialiser_pdo_ordered_statements($colonnes_statiques) {
1040
	return Array(
1093
	return Array(
1041
	    // insert_ligne_pattern_ordre
1094
	    // insert_ligne_pattern_ordre
1042
	    sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
1095
	    sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
1043
		    implode(', ', array_keys($colonnes_statiques)),
1096
		    implode(', ', array_keys($colonnes_statiques)),
1044
		    implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
1097
		    implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
1045
 
1098
 
1046
	    // insert_ligne_pattern_ordre
1099
	    // insert_ligne_pattern_ordre
1047
	    sprintf('(%s, %s ?)',
1100
	    sprintf('(%s, %s ?)',
1048
		    implode(', ', $colonnes_statiques),
1101
		    implode(', ', $colonnes_statiques),
1049
		    str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
1102
		    str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
1050
	);
1103
	);
1051
    }
1104
    }
1052
 
1105
 
1053
    static function initialiser_pdo_statements($colonnes_statiques) {
1106
    static function initialiser_pdo_statements($colonnes_statiques) {
1054
	return Array( 
1107
	return Array( 
1055
	    // insert_prefix
1108
	    // insert_prefix
1056
	    sprintf('INSERT INTO cel_obs (%s) VALUES ',
1109
	    sprintf('INSERT INTO cel_obs (%s) VALUES ',
1057
		    implode(', ', self::$ordre_BDD)),
1110
		    implode(', ', self::$ordre_BDD)),
1058
 
1111
 
1059
 
1112
 
1060
	    // insert_ligne_pattern, cf: self::$insert_ligne_pattern
1113
	    // insert_ligne_pattern, cf: self::$insert_ligne_pattern
1061
	    '(' .
1114
	    '(' .
1062
	    // 3) créé une chaîne de liste de champ à inséré en DB
1115
	    // 3) créé une chaîne de liste de champ à inséré en DB
1063
	    implode(', ', array_values(
1116
	    implode(', ', array_values(
1064
		// 2) garde les valeurs fixes (de $colonnes_statiques),
1117
		// 2) garde les valeurs fixes (de $colonnes_statiques),
1065
		// mais remplace les NULL par des "?"
1118
		// mais remplace les NULL par des "?"
1066
		array_map('__anonyme_5',
1119
		array_map('__anonyme_5',
1067
			  // 1) créé un tableau genre (nom_sel_nn => NULL) depuis self::$ordre_BDD
1120
			  // 1) créé un tableau genre (nom_sel_nn => NULL) depuis self::$ordre_BDD
1068
			  // et écrase certaines valeurs avec $colonnes_statiques (initilisé avec les données utilisateur)
1121
			  // et écrase certaines valeurs avec $colonnes_statiques (initilisé avec les données utilisateur)
1069
			  array_merge(array_map('__anonyme_6', array_flip(self::$ordre_BDD)), $colonnes_statiques
1122
			  array_merge(array_map('__anonyme_6', array_flip(self::$ordre_BDD)), $colonnes_statiques
1070
			  )))) .
1123
			  )))) .
1071
	    ')'
1124
	    ')'
1072
	);
1125
	);
1073
    }
1126
    }
1074
 
1127
 
1075
    // équivalent à Bdd2->proteger() (qui wrap PDO::quote),
1128
    // équivalent à Bdd2->proteger() (qui wrap PDO::quote),
1076
    // sans transformer NULL en ""
1129
    // sans transformer NULL en ""
1077
    static function quoteNonNull($chaine) {
1130
    static function quoteNonNull($chaine) {
1078
	if(is_null($chaine)) return "NULL";
1131
	if(is_null($chaine)) return "NULL";
1079
	if(!is_string($chaine) && !is_integer($chaine)) {
1132
	if(!is_string($chaine) && !is_integer($chaine)) {
1080
	    die("erreur: " . __FILE__ . ':' . __LINE__);
1133
	    die("erreur: " . __FILE__ . ':' . __LINE__);
1081
	}
1134
	}
1082
	return Cel::db()->quote($chaine);
1135
	return Cel::db()->quote($chaine);
1083
    }
1136
    }
1084
 
1137
 
1085
    public function erreurs_stock($errno, $errstr) {
1138
    public function erreurs_stock($errno, $errstr) {
1086
	$this->bilan[] = $errstr;
1139
	$this->bilan[] = $errstr;
1087
    }
1140
    }
1088
}
1141
}