Subversion Repositories eFlore/Applications.cel

Rev

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

Rev 1688 Rev 1697
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
 
40
 
41
// Numbers of days between January 1, 1900 and 1970 (including 19 leap years)
41
// Numbers of days between January 1, 1900 and 1970 (including 19 leap years)
42
// see traiterDateObs()
42
// see traiterDateObs()
43
// define("MIN_DATES_DIFF", 25569);
43
// define("MIN_DATES_DIFF", 25569);
44
 
44
 
45
 
45
 
46
class MyReadFilter implements PHPExcel_Reader_IReadFilter {
46
class MyReadFilter implements PHPExcel_Reader_IReadFilter {
47
	// exclusion de colonnes
47
	// exclusion de colonnes
48
	public $exclues = array();
48
	public $exclues = array();
49
 
49
 
50
	// lecture par morceaux
50
	// lecture par morceaux
51
    public $ligne_debut = 0; 
51
    public $ligne_debut = 0; 
52
    public $ligne_fin = 0;
52
    public $ligne_fin = 0;
53
 
53
 
54
	public function __construct() {}
54
	public function __construct() {}
55
	public function def_interval($debut, $nb) {
55
	public function def_interval($debut, $nb) {
56
		$this->ligne_debut = $debut;
56
		$this->ligne_debut = $debut;
57
		$this->ligne_fin = $debut + $nb;
57
		$this->ligne_fin = $debut + $nb;
58
	}
58
	}
59
    public function readCell($colonne, $ligne, $worksheetName = '') {
59
    public function readCell($colonne, $ligne, $worksheetName = '') {
60
		if(@$this->exclues[$colonne]) return false;
60
		if(@$this->exclues[$colonne]) return false;
61
		// si des n° de morceaux ont été initialisés, on filtre...
61
		// 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;
62
		if($this->ligne_debut && ($ligne < $this->ligne_debut || $ligne >= $this->ligne_fin)) return false;
63
		return true;
63
		return true;
64
    } 
64
    } 
65
} 
65
} 
66
 
66
 
67
// XXX: PHP 5.3
67
// XXX: PHP 5.3
68
function __anonyme_1($v) {	return !$v['importable']; }
68
function __anonyme_1($v) {	return !$v['importable']; }
69
function __anonyme_2(&$v) {	$v = $v['nom']; }
69
function __anonyme_2(&$v) {	$v = $v['nom']; }
70
function __anonyme_3($cell) { return !is_null($cell); };
70
function __anonyme_3($cell) { return !is_null($cell); };
71
function __anonyme_5($item) { return is_null($item) ? '?' : $item; }
71
function __anonyme_5($item) { return is_null($item) ? '?' : $item; }
72
function __anonyme_6() { return NULL; }
72
function __anonyme_6() { return NULL; }
73
 
73
 
74
class ImportXLS extends Cel  {
74
class ImportXLS extends Cel  {
75
	static function __anonyme_4(&$item, $key, $obj) { $item = $obj->quoteNonNull(trim($item)); }
75
	static function __anonyme_4(&$item, $key, $obj) { $item = $obj->quoteNonNull(trim($item)); }
76
 
76
 
77
	static $ordre_BDD = Array(
77
	static $ordre_BDD = Array(
78
		"ce_utilisateur",
78
		"ce_utilisateur",
79
		"prenom_utilisateur",
79
		"prenom_utilisateur",
80
		"nom_utilisateur",
80
		"nom_utilisateur",
81
		"courriel_utilisateur",
81
		"courriel_utilisateur",
82
		"ordre",
82
		"ordre",
83
		"nom_sel",
83
		"nom_sel",
84
		"nom_sel_nn",
84
		"nom_sel_nn",
85
		"nom_ret",
85
		"nom_ret",
86
		"nom_ret_nn",
86
		"nom_ret_nn",
87
		"nt",
87
		"nt",
88
		"famille",
88
		"famille",
89
		"nom_referentiel",
89
		"nom_referentiel",
90
		"zone_geo",
90
		"zone_geo",
91
		"ce_zone_geo",
91
		"ce_zone_geo",
92
		"date_observation",
92
		"date_observation",
93
		"lieudit",
93
		"lieudit",
94
		"station",
94
		"station",
95
		"milieu",
95
		"milieu",
96
		"mots_cles_texte",
96
		"mots_cles_texte",
97
		"commentaire",
97
		"commentaire",
98
		"transmission",
98
		"transmission",
99
		"date_creation",
99
		"date_creation",
100
		"date_modification",
100
		"date_modification",
101
		"date_transmission",
101
		"date_transmission",
102
		"latitude",
102
		"latitude",
103
		"longitude",
103
		"longitude",
104
		"abondance",
104
		"abondance",
105
		"certitude",
105
		"certitude",
106
		"phenologie",
106
		"phenologie",
107
		"code_insee_calcule"
107
		"code_insee_calcule"
108
	);
108
	);
109
 
109
 
110
	// cf: initialiser_pdo_ordered_statements()
110
	// cf: initialiser_pdo_ordered_statements()
111
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., phenologie, code_insee_calcule) VALUES"
111
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., phenologie, code_insee_calcule) VALUES"
112
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
112
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
113
	static $insert_prefix_ordre;
113
	static $insert_prefix_ordre;
114
	// eg: "(<id>, <prenom>, <nom>, <email>, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
114
	// eg: "(<id>, <prenom>, <nom>, <email>, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
115
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
115
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
116
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
116
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
117
	static $insert_ligne_pattern_ordre;
117
	static $insert_ligne_pattern_ordre;
118
 
118
 
119
	// seconde (meilleure) possibilité
119
	// seconde (meilleure) possibilité
120
	// cf: initialiser_pdo_statements()
120
	// cf: initialiser_pdo_statements()
121
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., date_creation, ...phenologie, code_insee_calcule) VALUES"
121
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., date_creation, ...phenologie, code_insee_calcule) VALUES"
122
	static $insert_prefix;
122
	static $insert_prefix;
123
	// eg: "(<id>, <prenom>, <nom>, <email>, ?, ?, ?, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
123
	// eg: "(<id>, <prenom>, <nom>, <email>, ?, ?, ?, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
124
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
124
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
125
	static $insert_ligne_pattern;
125
	static $insert_ligne_pattern;
126
 
126
 
127
	/*
127
	/*
128
	  Ces colonnes:
128
	  Ces colonnes:
129
	  - sont propres à l'ensemble des enregistrements uploadés
129
	  - sont propres à l'ensemble des enregistrements uploadés
130
	  - sont indépendantes du numéro de lignes
130
	  - sont indépendantes du numéro de lignes
131
	  - n'ont pas de valeur par défaut dans la structure de la table
131
	  - n'ont pas de valeur par défaut dans la structure de la table
132
	  - nécessitent une initialisation dans le cadre de l'upload
132
	  - nécessitent une initialisation dans le cadre de l'upload
133
 
133
 
134
	  initialiser_colonnes_statiques() y merge les données d'identification utilisateur
134
	  initialiser_colonnes_statiques() y merge les données d'identification utilisateur
135
	*/
135
	*/
136
	public $colonnes_statiques = Array(
136
	public $colonnes_statiques = Array(
137
		"ce_utilisateur" => NULL,
137
		"ce_utilisateur" => NULL,
138
		"prenom_utilisateur" => NULL,
138
		"prenom_utilisateur" => NULL,
139
		"nom_utilisateur" => NULL,
139
		"nom_utilisateur" => NULL,
140
		"courriel_utilisateur" => NULL,
140
		"courriel_utilisateur" => NULL,
141
 
141
 
142
		// fixes (fonction SQL)
142
		// fixes (fonction SQL)
143
		// XXX future: mais pourraient varier dans le futur si la mise-à-jour
143
		// XXX future: mais pourraient varier dans le futur si la mise-à-jour
144
		// d'observation est implémentée
144
		// d'observation est implémentée
145
		"date_creation" => "now()",
145
		"date_creation" => "now()",
146
		"date_modification" => "now()",
146
		"date_modification" => "now()",
147
	);
147
	);
148
 
148
 
149
	public $id_utilisateur = NULL;
149
	public $id_utilisateur = NULL;
150
 
150
 
151
	// erreurs d'import
151
	// erreurs d'import
152
	public $bilan = Array();
152
	public $bilan = Array();
153
 
153
 
154
 
154
 
155
	function ExportXLS($config) {
155
	function ExportXLS($config) {
156
		parent::__construct($config);
156
		parent::__construct($config);
157
	}
157
	}
158
 
158
 
159
	function createElement($pairs) {
159
	function createElement($pairs) {
160
		if(!isset($pairs['utilisateur']) || trim($pairs['utilisateur']) == '') {
160
		if(!isset($pairs['utilisateur']) || trim($pairs['utilisateur']) == '') {
161
			echo '0'; exit;
161
			echo '0'; exit;
162
		}
162
		}
163
 
163
 
164
		$id_utilisateur = intval($pairs['utilisateur']);
164
		$id_utilisateur = intval($pairs['utilisateur']);
165
		$this->id_utilisateur = $id_utilisateur; // pour traiterImage();
165
		$this->id_utilisateur = $id_utilisateur; // pour traiterImage();
166
 
166
 
167
		if(!isset($_SESSION)) session_start();
167
		if(!isset($_SESSION)) session_start();
168
        $this->controleUtilisateur($id_utilisateur);
168
        $this->controleUtilisateur($id_utilisateur);
169
 
169
 
170
        $this->utilisateur = $this->getInfosComplementairesUtilisateur($id_utilisateur);
170
        $this->utilisateur = $this->getInfosComplementairesUtilisateur($id_utilisateur);
171
 
171
 
172
		$this->initialiser_colonnes_statiques($id_utilisateur);
172
		$this->initialiser_colonnes_statiques($id_utilisateur);
173
 
173
 
174
		// initialisation du statement PDO/MySQL
174
		// initialisation du statement PDO/MySQL
175
		// première version, pattern de requête pas génial
175
		// première version, pattern de requête pas génial
176
		/* list(self;;$insert_prefix_ordre, self::$insert_ligne_pattern_ordre) =
176
		/* list(self;;$insert_prefix_ordre, self::$insert_ligne_pattern_ordre) =
177
		   $this->initialiser_pdo_ordered_statements($this->colonnes_statiques); */
177
		   $this->initialiser_pdo_ordered_statements($this->colonnes_statiques); */
178
		list(self::$insert_prefix, self::$insert_ligne_pattern) =
178
		list(self::$insert_prefix, self::$insert_ligne_pattern) =
179
			$this->initialiser_pdo_statements($this->colonnes_statiques);
179
			$this->initialiser_pdo_statements($this->colonnes_statiques);
180
 
180
 
181
 
181
 
182
		$infos_fichier = array_pop($_FILES);
182
		$infos_fichier = array_pop($_FILES);
183
		
183
		
184
		/*$objPHPExcel = PHPExcel_IOFactory::load($infos_fichier['tmp_name']);
184
		/*$objPHPExcel = PHPExcel_IOFactory::load($infos_fichier['tmp_name']);
185
		  $donnees = $objPHPExcel->getActiveSheet()->toArray(NULL,FALSE,FALSE,TRUE);*/
185
		  $donnees = $objPHPExcel->getActiveSheet()->toArray(NULL,FALSE,FALSE,TRUE);*/
186
 
186
 
187
		/*$objReader = PHPExcel_IOFactory::createReader("Excel5");
187
		/*$objReader = PHPExcel_IOFactory::createReader("Excel5");
188
		$objReader->setReadDataOnly(true);
188
		$objReader->setReadDataOnly(true);
189
		$objPHPExcel = $objReader->load($infos_fichier['tmp_name']);*/
189
		$objPHPExcel = $objReader->load($infos_fichier['tmp_name']);*/
190
 
190
 
191
		//var_dump($donnees);
191
		//var_dump($donnees);
192
 
192
 
193
		// renomme le fichier pour lui ajouter son extension initiale, ce qui
193
		// renomme le fichier pour lui ajouter son extension initiale, ce qui
194
		// permet (une sorte) d'autodétection du format.
194
		// permet (une sorte) d'autodétection du format.
195
		$fichier = $infos_fichier['tmp_name'];
195
		$fichier = $infos_fichier['tmp_name'];
196
		$extension = pathinfo($infos_fichier['name'], PATHINFO_EXTENSION);
196
		$extension = pathinfo($infos_fichier['name'], PATHINFO_EXTENSION);
197
		if( (strlen($extension) == 3 || strlen($extension) == 4) &&
197
		if( (strlen($extension) == 3 || strlen($extension) == 4) &&
198
			(rename($fichier, $fichier . '.' . $extension))) {
198
			(rename($fichier, $fichier . '.' . $extension))) {
199
			$fichier = $fichier . '.' . $extension;
199
			$fichier = $fichier . '.' . $extension;
200
		}
200
		}
201
 
201
 
202
		$objReader = PHPExcel_IOFactory::createReaderForFile($fichier);
202
		$objReader = PHPExcel_IOFactory::createReaderForFile($fichier);
203
		$objReader->setReadDataOnly(true);
203
		$objReader->setReadDataOnly(true);
204
 
204
 
205
		if(is_a($objReader, 'PHPExcel_Reader_CSV')) {
205
		if(is_a($objReader, 'PHPExcel_Reader_CSV')) {
206
			$objReader->setDelimiter(',')
206
			$objReader->setDelimiter(',')
207
				->setEnclosure('"')
207
				->setEnclosure('"')
208
				->setLineEnding("\n")
208
				->setLineEnding("\n")
209
				->setSheetIndex(0);
209
				->setSheetIndex(0);
210
		}
210
		}
211
 
211
 
212
		// on ne conserve que l'en-tête
212
		// on ne conserve que l'en-tête
213
		$filtre = new MyReadFilter();
213
		$filtre = new MyReadFilter();
214
		$filtre->def_interval(1, 2);
214
		$filtre->def_interval(1, 2);
215
		$objReader->setReadFilter($filtre);
215
		$objReader->setReadFilter($filtre);
216
 
216
 
217
		$objPHPExcel = $objReader->load($fichier);
217
		$objPHPExcel = $objReader->load($fichier);
218
		$obj_infos = $objReader->listWorksheetInfo($fichier);
218
		$obj_infos = $objReader->listWorksheetInfo($fichier);
219
		// XXX: indépendant du readFilter ?
219
		// XXX: indépendant du readFilter ?
220
		$nb_lignes = $obj_infos[0]['totalRows'];
220
		$nb_lignes = $obj_infos[0]['totalRows'];
221
 
221
 
222
		$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, FALSE, TRUE);
222
		$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, FALSE, TRUE);
223
		$filtre->exclues = self::detectionEntete($donnees[1]);
223
		$filtre->exclues = self::detectionEntete($donnees[1]);
224
 
224
 
225
		$obs_ajouts = 0;
225
		$obs_ajouts = 0;
226
		$obs_maj = 0;
226
		$obs_maj = 0;
227
		$nb_images_ajoutees = 0;
227
		$nb_images_ajoutees = 0;
228
		$nb_mots_cle_ajoutes = 0;
228
		$nb_mots_cle_ajoutes = 0;
229
 
229
 
230
		$dernier_ordre = $this->requeter("SELECT MAX(ordre) AS ordre FROM cel_obs WHERE ce_utilisateur = $id_utilisateur");
230
		$dernier_ordre = $this->requeter("SELECT MAX(ordre) AS ordre FROM cel_obs WHERE ce_utilisateur = $id_utilisateur");
231
		$dernier_ordre = intval($dernier_ordre[0]['ordre']) + 1;
231
		$dernier_ordre = intval($dernier_ordre[0]['ordre']) + 1;
232
		if(! $dernier_ordre) $dernier_ordre = 0;
232
		if(! $dernier_ordre) $dernier_ordre = 0;
233
 
233
 
234
		// on catch to les trigger_error(E_USER_NOTICE);
234
		// on catch to les trigger_error(E_USER_NOTICE);
235
		set_error_handler(array($this, 'erreurs_stock'), E_USER_NOTICE);
235
		set_error_handler(array($this, 'erreurs_stock'), E_USER_NOTICE);
236
 
236
 
237
		// lecture par morceaux (chunks), NB_LIRE_LIGNE_SIMUL lignes à fois
237
		// lecture par morceaux (chunks), NB_LIRE_LIGNE_SIMUL lignes à fois
238
		// pour aboutir des requêtes SQL d'insert groupés.
238
		// pour aboutir des requêtes SQL d'insert groupés.
239
		for($ligne = 2; $ligne < $nb_lignes + NB_LIRE_LIGNE_SIMUL; $ligne += NB_LIRE_LIGNE_SIMUL) {
239
		for($ligne = 2; $ligne < $nb_lignes + NB_LIRE_LIGNE_SIMUL; $ligne += NB_LIRE_LIGNE_SIMUL) {
240
			$filtre->def_interval($ligne, NB_LIRE_LIGNE_SIMUL);
240
			$filtre->def_interval($ligne, NB_LIRE_LIGNE_SIMUL);
241
			$objReader->setReadFilter($filtre);
241
			$objReader->setReadFilter($filtre);
242
 
242
 
243
			/* recharge avec $filtre actif (filtre sur lignes colonnes):
243
			/* recharge avec $filtre actif (filtre sur lignes colonnes):
244
			   - exclue les colonnes inutiles/inutilisables)
244
			   - exclue les colonnes inutiles/inutilisables)
245
			   - ne selectionne que les lignes dans le range [$ligne - $ligne + NB_LIRE_LIGNE_SIMUL] */
245
			   - ne selectionne que les lignes dans le range [$ligne - $ligne + NB_LIRE_LIGNE_SIMUL] */
246
			$objPHPExcel = $objReader->load($fichier);
246
			$objPHPExcel = $objReader->load($fichier);
247
			$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, FALSE, TRUE);
247
			$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, FALSE, TRUE);
248
 
248
 
249
			// ici on appel la fonction qui fera effectivement l'insertion multiple
249
			// ici on appel la fonction qui fera effectivement l'insertion multiple
250
			// à partir des (au plus) NB_LIRE_LIGNE_SIMUL lignes
250
			// à partir des (au plus) NB_LIRE_LIGNE_SIMUL lignes
251
 
251
 
252
			// TODO: passer $this, ne sert que pour appeler des méthodes publiques qui pourraient être statiques
252
			// TODO: passer $this, ne sert que pour appeler des méthodes publiques qui pourraient être statiques
253
			// notamment dans RechercheInfosTaxonBeta.php
253
			// notamment dans RechercheInfosTaxonBeta.php
254
			list($enregistrements, $images, $mots_cle) =
254
			list($enregistrements, $images, $mots_cle) =
255
				self::chargerLignes($this, $donnees, $this->colonnes_statiques, $dernier_ordre);
255
				self::chargerLignes($this, $donnees, $this->colonnes_statiques, $dernier_ordre);
256
			if(! $enregistrements) break;
256
			if(! $enregistrements) break;
257
 
257
 
258
			self::trierColonnes($enregistrements);
258
			self::trierColonnes($enregistrements);
259
			// normalement: NB_LIRE_LIGNE_SIMUL, sauf si une enregistrement ne semble pas valide
259
			// normalement: NB_LIRE_LIGNE_SIMUL, sauf si une enregistrement ne semble pas valide
260
			// ou bien lors du dernier chunk
260
			// ou bien lors du dernier chunk
261
 
261
 
262
			$nb_rec = count($enregistrements);
262
			$nb_rec = count($enregistrements);
263
			$sql_pattern = self::$insert_prefix .
263
			$sql_pattern = self::$insert_prefix .
264
				str_repeat(self::$insert_ligne_pattern_ordre . ', ', $nb_rec - 1) .
264
				str_repeat(self::$insert_ligne_pattern_ordre . ', ', $nb_rec - 1) .
265
				self::$insert_ligne_pattern_ordre;
265
				self::$insert_ligne_pattern_ordre;
266
 
266
 
267
			$sql_pattern = self::$insert_prefix .
267
			$sql_pattern = self::$insert_prefix .
268
				str_repeat(self::$insert_ligne_pattern . ', ', $nb_rec - 1) .
268
				str_repeat(self::$insert_ligne_pattern . ', ', $nb_rec - 1) .
269
				self::$insert_ligne_pattern;
269
				self::$insert_ligne_pattern;
270
 
270
 
271
			$this->bdd->beginTransaction();
271
			$this->bdd->beginTransaction();
272
			$stmt = $this->bdd->prepare($sql_pattern);
272
			$stmt = $this->bdd->prepare($sql_pattern);
273
			$donnees = array();
273
			$donnees = array();
274
			foreach($enregistrements as $e) $donnees = array_merge($donnees, array_values($e));
274
			foreach($enregistrements as $e) $donnees = array_merge($donnees, array_values($e));
275
 
275
 
276
			/* debug ici: echo $sql_pattern . "\n"; var_dump($enregistrements, $donnees); die;*/
276
			/* debug ici: echo $sql_pattern . "\n"; var_dump($enregistrements, $donnees); die;*/
277
 
277
 
278
			$stmt->execute($donnees);
278
			$stmt->execute($donnees);
279
 
279
 
280
			// $stmt->debugDumpParams(); // https://bugs.php.net/bug.php?id=52384
280
			// $stmt->debugDumpParams(); // https://bugs.php.net/bug.php?id=52384
281
			$dernier_autoinc = $this->bdd->lastInsertId();
281
			$dernier_autoinc = $this->bdd->lastInsertId();
282
			$this->bdd->commit();
282
			$this->bdd->commit();
283
 
283
 
284
			if(! $dernier_autoinc) trigger_error("l'insertion semble avoir échoué", E_USER_NOTICE);
284
			if(! $dernier_autoinc) trigger_error("l'insertion semble avoir échoué", E_USER_NOTICE);
285
 
285
 
286
			$obs_ajouts += count($enregistrements);
286
			$obs_ajouts += count($enregistrements);
287
			// $obs_ajouts += count($enregistrements['insert']);
287
			// $obs_ajouts += count($enregistrements['insert']);
288
			// $obs_maj += count($enregistrements['update']);
288
			// $obs_maj += count($enregistrements['update']);
289
			$nb_images_ajoutees += self::stockerImages($this, $enregistrements, $images, $dernier_autoinc);
289
			$nb_images_ajoutees += self::stockerImages($this, $enregistrements, $images, $dernier_autoinc);
290
			$nb_mots_cle_ajoutes += self::stockerMotsCle($this, $enregistrements, $mots_cle, $dernier_autoinc);
290
			$nb_mots_cle_ajoutes += self::stockerMotsCle($this, $enregistrements, $mots_cle, $dernier_autoinc);
291
		}
291
		}
292
 
292
 
293
		restore_error_handler();
293
		restore_error_handler();
294
 
294
 
295
		if($this->bilan) echo implode("\n", $this->bilan) . "\n";
295
		if($this->bilan) echo implode("\n", $this->bilan) . "\n";
296
 		$summary = sprintf("%d observation(s) ajoutée(s)\n%d image(s) attachée(s)\n%d mot(s)-clé ajouté(s) [TODO]\n",
296
 		$summary = sprintf("%d observation(s) ajoutée(s)\n%d image(s) attachée(s)\n%d mot(s)-clé ajouté(s) [TODO]\n",
297
						   $obs_ajouts,
297
						   $obs_ajouts,
298
						   $nb_images_ajoutees,
298
						   $nb_images_ajoutees,
299
						   $nb_mots_cle_ajoutes);
299
						   $nb_mots_cle_ajoutes);
300
 
300
 
301
		die("$summary");
301
		die("$summary");
302
	}
302
	}
303
 
303
 
304
	static function detectionEntete($entete) {
304
	static function detectionEntete($entete) {
305
		$colonnes_reconnues = Array();
305
		$colonnes_reconnues = Array();
306
		$cols = FormateurGroupeColonne::nomEnsembleVersListeColonnes('standard,avance');
306
		$cols = FormateurGroupeColonne::nomEnsembleVersListeColonnes('standard,avance');
307
		foreach($entete as $k => $v) {
307
		foreach($entete as $k => $v) {
308
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
308
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
309
			foreach($cols as $col) {
309
			foreach($cols as $col) {
310
				$entete_officiel_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($col['nom'])));
310
				$entete_officiel_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($col['nom'])));
311
				$entete_officiel_abbrev = $col['abbrev'];
311
				$entete_officiel_abbrev = $col['abbrev'];
312
				if($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
312
				if($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
313
					// debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
313
					// debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
314
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
314
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
315
					$colonnes_reconnues[$k] = 1;
315
					$colonnes_reconnues[$k] = 1;
316
					break;
316
					break;
317
				}
317
				}
318
			}
318
			}
319
		}
319
		}
320
 
320
 
321
		// prépare le filtre de PHPExcel qui évitera le traitement de toutes les colonnes superflues
321
		// prépare le filtre de PHPExcel qui évitera le traitement de toutes les colonnes superflues
322
 
322
 
323
		// eg: diff ( Array( H => Commune, I => rien ) , Array( H => 1, K => 1 )
323
		// eg: diff ( Array( H => Commune, I => rien ) , Array( H => 1, K => 1 )
324
		// ==> Array( I => rien )
324
		// ==> Array( I => rien )
325
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
325
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
326
 
326
 
327
		// des colonnes de FormateurGroupeColonne::nomEnsembleVersListeColonnes()
327
		// des colonnes de FormateurGroupeColonne::nomEnsembleVersListeColonnes()
328
		// ne retient que celles marquées "importables"
328
		// ne retient que celles marquées "importables"
329
		$colonnes_automatiques = array_filter($cols, '__anonyme_1');
329
		$colonnes_automatiques = array_filter($cols, '__anonyme_1');
330
 
330
 
331
		// ne conserve que le nom long pour matcher avec la ligne XLS d'entête
331
		// ne conserve que le nom long pour matcher avec la ligne XLS d'entête
332
		array_walk($colonnes_automatiques, '__anonyme_2');
332
		array_walk($colonnes_automatiques, '__anonyme_2');
333
 
333
 
334
		// intersect ( Array ( N => Milieu, S => Ordre ), Array ( ordre => Ordre, phenologie => Phénologie ) )
334
		// intersect ( Array ( N => Milieu, S => Ordre ), Array ( ordre => Ordre, phenologie => Phénologie ) )
335
		// ==> Array ( S => Ordre, AA => Phénologie )
335
		// ==> Array ( S => Ordre, AA => Phénologie )
336
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
336
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
337
 
337
 
338
		// TODO: pourquoi ne pas comparer avec les abbrevs aussi ?
338
		// TODO: pourquoi ne pas comparer avec les abbrevs aussi ?
339
		// merge ( Array( I => rien ) , Array ( S => Ordre, AA => Phénologie ) )
339
		// merge ( Array( I => rien ) , Array ( S => Ordre, AA => Phénologie ) )
340
		// ==> Array ( I => rien, AA => Phénologie )
340
		// ==> Array ( I => rien, AA => Phénologie )
341
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
341
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
342
	}
342
	}
343
 
343
 
344
	/*
344
	/*
345
	 * charge un groupe de lignes
345
	 * charge un groupe de lignes
346
	 */
346
	 */
347
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
347
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
348
		$enregistrement = NULL;
348
		$enregistrement = NULL;
349
		$enregistrements = Array();
349
		$enregistrements = Array();
350
		$toutes_images = Array();
350
		$toutes_images = Array();
351
		$tous_mots_cle = Array();
351
		$tous_mots_cle = Array();
352
 
352
 
353
		foreach($lignes as $ligne) {
353
		foreach($lignes as $ligne) {
354
			//$ligne = array_filter($ligne, function($cell) { return !is_null($cell); });
354
			//$ligne = array_filter($ligne, function($cell) { return !is_null($cell); });
355
			//if(!$ligne) continue;
355
			//if(!$ligne) continue;
356
			// on a besoin des NULL pour éviter des notice d'index indéfini
356
			// on a besoin des NULL pour éviter des notice d'index indéfini
357
			if(! array_filter($ligne, '__anonyme_3')) continue;
357
			if(! array_filter($ligne, '__anonyme_3')) continue;
358
 
358
 
359
			if( ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) ) {
359
			if( ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) ) {
360
				// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
360
				// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
361
				$enregistrements[] = $enregistrement;
361
				$enregistrements[] = $enregistrement;
362
				$pos = count($enregistrements) - 1;
362
				$pos = count($enregistrements) - 1;
363
				$last = &$enregistrements[$pos];
363
				$last = &$enregistrements[$pos];
364
 
364
 
365
				if(isset($enregistrement['_images'])) {
365
				if(isset($enregistrement['_images'])) {
366
					// ne dépend pas de cel_obs, et seront insérées *après* les enregistrements
366
					// ne dépend pas de cel_obs, et seront insérées *après* les enregistrements
367
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
367
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
368
					$toutes_images[] = Array("images" => $last['_images'],
368
					$toutes_images[] = Array("images" => $last['_images'],
369
											 "obs_pos" => $pos);
369
											 "obs_pos" => $pos);
370
					// ce champ n'a pas à faire partie de l'insertion dans cel_obs,
370
					// ce champ n'a pas à faire partie de l'insertion dans cel_obs,
371
					// mais est utile pour cel_obs_images
371
					// mais est utile pour cel_obs_images
372
					unset($last['_images']);
372
					unset($last['_images']);
373
				}
373
				}
374
 
374
 
375
				if(isset($enregistrement['_mots_cle'])) {
375
				if(isset($enregistrement['_mots_cle'])) {
376
					// ne dépend pas de cel_obs, et seront insérés *après* les enregistrements
376
					// ne dépend pas de cel_obs, et seront insérés *après* les enregistrements
377
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
377
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
378
					$tous_mots_cle[] = Array("mots_cle" => $last['_mots_cle'],
378
					$tous_mots_cle[] = Array("mots_cle" => $last['_mots_cle'],
379
											 "obs_pos" => $pos);
379
											 "obs_pos" => $pos);
380
					// la version inlinée des mots est enregistrées dans cel_obs
380
					// la version inlinée des mots est enregistrées dans cel_obs
381
					// mais cel_mots_cles_obs fait foi.
381
					// mais cel_mots_cles_obs fait foi.
382
					// XXX: postponer l'ajout de ces informations dans cel_obs *après* l'insertion effective
382
					// XXX: postponer l'ajout de ces informations dans cel_obs *après* l'insertion effective
383
					// des records dans cel_mots_cles_obs ?
383
					// des records dans cel_mots_cles_obs ?
384
					unset($last['_mots_cle']);
384
					unset($last['_mots_cle']);
385
				}
385
				}
386
 
386
 
387
				$dernier_ordre++;
387
				$dernier_ordre++;
388
			}
388
			}
389
		}
389
		}
390
 
390
 
391
		// XXX future: return Array($enregistrements_a_inserer, $enregistrements_a_MAJ, $toutes_images);
391
		// XXX future: return Array($enregistrements_a_inserer, $enregistrements_a_MAJ, $toutes_images);
392
		return Array($enregistrements, $toutes_images, $tous_mots_cle);
392
		return Array($enregistrements, $toutes_images, $tous_mots_cle);
393
	}
393
	}
394
 
394
 
395
 
395
 
396
	static function trierColonnes(&$enregistrements) {
396
	static function trierColonnes(&$enregistrements) {
397
		foreach($enregistrements as &$enregistrement) {
397
		foreach($enregistrements as &$enregistrement) {
398
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
398
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
399
			//array_walk($enregistrement, function(&$item, $k) { $item = is_null($item) ? "NULL" : $item; });
399
			//array_walk($enregistrement, function(&$item, $k) { $item = is_null($item) ? "NULL" : $item; });
400
			//$req .= implode(', ', $enregistrement) . "\n";
400
			//$req .= implode(', ', $enregistrement) . "\n";
401
		}
401
		}
402
	}
402
	}
403
 
403
 
404
 
404
 
405
	static function stockerMotsCle($cel, $enregistrements, $tous_mots_cle, $lastid) {
405
	static function stockerMotsCle($cel, $enregistrements, $tous_mots_cle, $lastid) {
406
		$c = 0;
406
		$c = 0;
407
		// debug: var_dump($tous_mots_cle);die;
407
		// debug: var_dump($tous_mots_cle);die;
408
		foreach($tous_mots_cle as $v) $c += count($v['mots_cle']['to_insert']);
408
		foreach($tous_mots_cle as $v) $c += count($v['mots_cle']['to_insert']);
409
		return $c;
409
		return $c;
410
	}
410
	}
411
 
411
 
412
	static function stockerImages($cel, $enregistrements, $toutes_images, $lastid) {
412
	static function stockerImages($cel, $enregistrements, $toutes_images, $lastid) {
413
		$images_insert = 'INSERT INTO cel_obs_images (id_image, id_observation) VALUES %s ON DUPLICATE KEY UPDATE id_image = id_image';
413
		$images_insert = 'INSERT INTO cel_obs_images (id_image, id_observation) VALUES %s ON DUPLICATE KEY UPDATE id_image = id_image';
414
		$images_obs_assoc = Array();
414
		$images_obs_assoc = Array();
415
 
415
 
416
		foreach($toutes_images as $images_pour_obs) {
416
		foreach($toutes_images as $images_pour_obs) {
417
			$obs = $enregistrements[$images_pour_obs["obs_pos"]];
417
			$obs = $enregistrements[$images_pour_obs["obs_pos"]];
418
			$id_obs = $lastid // dernier autoinc inséré
418
			$id_obs = $lastid // dernier autoinc inséré
419
				- count($enregistrements) + 1 // correspondrait au premier autoinc
419
				- count($enregistrements) + 1 // correspondrait au premier autoinc
420
				+ $images_pour_obs["obs_pos"]; // ordre d'insertion = ordre dans le tableau $enregistrements (commence à 0)
420
				+ $images_pour_obs["obs_pos"]; // ordre d'insertion = ordre dans le tableau $enregistrements (commence à 0)
421
			foreach($images_pour_obs['images'] as $image) {
421
			foreach($images_pour_obs['images'] as $image) {
422
				$images_obs_assoc[] = sprintf('(%d,%d)',
422
				$images_obs_assoc[] = sprintf('(%d,%d)',
423
											  $image['id_image'], // intval() useless
423
											  $image['id_image'], // intval() useless
424
											  $id_obs); // intval() useless
424
											  $id_obs); // intval() useless
425
			}
425
			}
426
		}
426
		}
427
 
427
 
428
		if($images_obs_assoc) {
428
		if($images_obs_assoc) {
429
			$requete = sprintf($images_insert, implode(', ', $images_obs_assoc));
429
			$requete = sprintf($images_insert, implode(', ', $images_obs_assoc));
430
			// debug echo "$requete\n";
430
			// debug echo "$requete\n";
431
			$cel->requeter($requete);
431
			$cel->requeter($requete);
432
		}
432
		}
433
 
433
 
434
		return count($images_obs_assoc);
434
		return count($images_obs_assoc);
435
	}
435
	}
436
 
436
 
437
	/*
437
	/*
438
	  Aucune des valeurs présentes dans $enregistrement n'est quotée
438
	  Aucune des valeurs présentes dans $enregistrement n'est quotée
439
	  cad aucune des valeurs retournée par traiter{Espece|Localisation}()
439
	  cad aucune des valeurs retournée par traiter{Espece|Localisation}()
440
	  car ce tableau est passé à un PDO::preparedStatement() qui applique
440
	  car ce tableau est passé à un PDO::preparedStatement() qui applique
441
	  proprement les règle d'échappement.
441
	  proprement les règle d'échappement.
442
	 */
442
	 */
443
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
443
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
444
		// en premier car le résultat est utile pour
444
		// en premier car le résultat est utile pour
445
		// traiter longitude et latitude (traiterLonLat())
445
		// traiter longitude et latitude (traiterLonLat())
446
		$referentiel = self::identReferentiel($ligne[C_NOM_REFERENTIEL]);
446
		$referentiel = self::identReferentiel($ligne[C_NOM_REFERENTIEL]);
447
 
447
 
448
		// $espece est rempli de plusieurs informations
448
		// $espece est rempli de plusieurs informations
449
		$espece = Array(C_NOM_SEL => NULL, C_NOM_SEL_NN => NULL, C_NOM_RET => NULL,
449
		$espece = Array(C_NOM_SEL => NULL, C_NOM_SEL_NN => NULL, C_NOM_RET => NULL,
450
						C_NOM_RET_NN => NULL, C_NT => NULL, C_FAMILLE => NULL);
450
						C_NOM_RET_NN => NULL, C_NT => NULL, C_FAMILLE => NULL);
451
		self::traiterEspece($ligne, $espece, $cel);
451
		self::traiterEspece($ligne, $espece, $cel);
452
 
452
 
453
		// $localisation est rempli à partir de plusieurs champs: C_ZONE_GEO et C_CE_ZONE_GEO
453
		// $localisation est rempli à partir de plusieurs champs: C_ZONE_GEO et C_CE_ZONE_GEO
454
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
454
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
455
		self::traiterLocalisation($ligne, $localisation, $cel);
455
		self::traiterLocalisation($ligne, $localisation, $cel);
456
 
456
 
457
		// $transmission est utilisé pour date_transmission
457
		// $transmission est utilisé pour date_transmission
458
		// XXX: @ contre "Undefined index"
458
		// XXX: @ contre "Undefined index"
459
		@$transmission = in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0;
459
		@$transmission = in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0;
460
 
460
 
461
 
461
 
462
		// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
462
		// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
463
		// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
463
		// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
464
		$enregistrement = Array(
464
		$enregistrement = Array(
465
			"ordre" => $dernier_ordre,
465
			"ordre" => $dernier_ordre,
466
 
466
 
467
			"nom_sel" => $espece[C_NOM_SEL],
467
			"nom_sel" => $espece[C_NOM_SEL],
468
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
468
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
469
			"nom_ret" => $espece[C_NOM_RET],
469
			"nom_ret" => $espece[C_NOM_RET],
470
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
470
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
471
			"nt" => $espece[C_NT],
471
			"nt" => $espece[C_NT],
472
			"famille" => $espece[C_FAMILLE],
472
			"famille" => $espece[C_FAMILLE],
473
 
473
 
474
			"nom_referentiel" => $referentiel,
474
			"nom_referentiel" => $referentiel,
475
 
475
 
476
			"zone_geo" => $localisation[C_ZONE_GEO],
476
			"zone_geo" => $localisation[C_ZONE_GEO],
477
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
477
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
478
 
478
 
479
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
479
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
480
			"date_observation" => self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ligne),
480
			"date_observation" => self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ligne),
481
 
481
 
482
			"lieudit" => trim($ligne[C_LIEUDIT]),
482
			"lieudit" => trim($ligne[C_LIEUDIT]),
483
			"station" => trim($ligne[C_STATION]),
483
			"station" => trim($ligne[C_STATION]),
484
			"milieu" => trim($ligne[C_MILIEU]),
484
			"milieu" => trim($ligne[C_MILIEU]),
485
 
485
 
486
			"mots_cles_texte" => NULL, // TODO: foreign-key
486
			"mots_cles_texte" => NULL, // TODO: foreign-key
487
			// XXX: @ contre "Undefined index"
487
			// XXX: @ contre "Undefined index"
488
			"commentaire" => @trim($ligne[C_COMMENTAIRE]),
488
			"commentaire" => @trim($ligne[C_COMMENTAIRE]),
489
 
489
 
490
			"transmission" => $transmission,
490
			"transmission" => $transmission,
491
			"date_transmission" => $transmission ? date("Y-m-d H:i:s") : NULL, // pas de fonction SQL dans un PDO statement, <=> now()
491
			"date_transmission" => $transmission ? date("Y-m-d H:i:s") : NULL, // pas de fonction SQL dans un PDO statement, <=> now()
492
 
492
 
493
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
493
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
494
			"latitude" => self::traiterLonLat(NULL, $ligne[C_LATITUDE], $referentiel, $ligne),
494
			"latitude" => self::traiterLonLat(NULL, $ligne[C_LATITUDE], $referentiel, $ligne),
495
			"longitude" => self::traiterLonLat($ligne[C_LONGITUDE], NULL, $referentiel, $ligne),
495
			"longitude" => self::traiterLonLat($ligne[C_LONGITUDE], NULL, $referentiel, $ligne),
496
 
496
 
497
			// @ car potentiellement optionnelles ou toutes vides => pas d'index dans PHPExcel (tableau optimisé)
497
			// @ car potentiellement optionnelles ou toutes vides => pas d'index dans PHPExcel (tableau optimisé)
498
			"abondance" => @$ligne[C_ABONDANCE],
498
			"abondance" => @$ligne[C_ABONDANCE],
499
			"certitude" => @$ligne[C_CERTITUDE],
499
			"certitude" => @$ligne[C_CERTITUDE],
500
			"phenologie" => @$ligne[C_PHENOLOGIE],
500
			"phenologie" => @$ligne[C_PHENOLOGIE],
501
 
501
 
502
			"code_insee_calcule" => substr($localisation[C_CE_ZONE_GEO], -5) // varchar(5)
502
			"code_insee_calcule" => substr($localisation[C_CE_ZONE_GEO], -5) // varchar(5)
503
		);
503
		);
504
 
504
 
505
		// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
505
		// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
506
		// que si des résultats sont trouvés
506
		// que si des résultats sont trouvés
507
		// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
507
		// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
508
		if(@$ligne[C_IMAGES]) self::traiterImage($ligne[C_IMAGES], $cel, $enregistrement);
508
		if(@$ligne[C_IMAGES]) self::traiterImage($ligne[C_IMAGES], $cel, $enregistrement);
509
 
509
 
510
		if(@$ligne[C_MOTS_CLES_TEXTE]) self::traiterMotsCle($ligne[C_MOTS_CLES_TEXTE], $cel, $enregistrement);
510
		if(@$ligne[C_MOTS_CLES_TEXTE]) self::traiterMotsCle($ligne[C_MOTS_CLES_TEXTE], $cel, $enregistrement);
511
 
511
 
512
		return $enregistrement;
512
		return $enregistrement;
513
	}
513
	}
514
 
514
 
515
	static function traiterImage($str, $cel, &$enregistrement) {
515
	static function traiterImage($str, $cel, &$enregistrement) {
516
		$liste_images = array_filter(explode("/", $str));
516
		$liste_images = array_filter(explode("/", $str));
517
 
517
 
518
		//array_walk($liste_images, '__anonyme_4', $cel);
518
		//array_walk($liste_images, '__anonyme_4', $cel);
519
		array_walk($liste_images, array(__CLASS__, '__anonyme_4'), $cel);
519
		array_walk($liste_images, array(__CLASS__, '__anonyme_4'), $cel);
520
		$requete = sprintf(
520
		$requete = sprintf(
521
			"SELECT id_image, nom_original FROM cel_images WHERE ce_utilisateur = %d AND nom_original IN (\"%s\")",
521
			"SELECT id_image, nom_original FROM cel_images WHERE ce_utilisateur = %d AND nom_original IN (\"%s\")",
522
			$cel->id_utilisateur,
522
			$cel->id_utilisateur,
523
			implode('","', $liste_images));
523
			implode('","', $liste_images));
524
 
524
 
525
		$resultat = $cel->requeter($requete);
525
		$resultat = $cel->requeter($requete);
526
 
526
 
527
		if($resultat) $enregistrement['_images'] = $resultat;
527
		if($resultat) $enregistrement['_images'] = $resultat;
528
	}
528
	}
529
 
529
 
530
	static function traiterMotsCle($str, $cel, &$enregistrement) {
530
	static function traiterMotsCle($str, $cel, &$enregistrement) {
531
		$liste_mots_cle = $liste_mots_cle_recherche = array_map("trim", array_unique(array_filter(explode(",", $str))));
531
		$liste_mots_cle = $liste_mots_cle_recherche = array_map("trim", array_unique(array_filter(explode(",", $str))));
532
		array_walk($liste_mots_cle_recherche, array(__CLASS__, '__anonyme_4'), $cel);
532
		array_walk($liste_mots_cle_recherche, array(__CLASS__, '__anonyme_4'), $cel);
533
 
533
 
534
		// TODO!!!! remplace > (pour les tests uniquement) par un = et supprimer le group by mot_cle
534
		// TODO!!!! remplace > (pour les tests uniquement) par un = et supprimer le group by mot_cle
535
		$requete = sprintf("SELECT id_mot_cle_obs, mot_cle FROM cel_mots_cles_obs WHERE id_utilisateur > %d ".
535
		$requete = sprintf("SELECT id_mot_cle_obs, mot_cle FROM cel_mots_cles_obs WHERE id_utilisateur > %d ".
536
						   "AND mot_cle IN (%s) ".
536
						   "AND mot_cle IN (%s) ".
537
						   "GROUP BY mot_cle",
537
						   "GROUP BY mot_cle",
538
						   $cel->id_utilisateur,
538
						   $cel->id_utilisateur,
539
						   implode(',', $liste_mots_cle_recherche));
539
						   implode(',', $liste_mots_cle_recherche));
540
 
540
 
541
		$resultat_sql = $cel->requeter($requete);
541
		$resultat_sql = $cel->requeter($requete);
542
		if(!$resultat_sql) return;
542
		if(!$resultat_sql) return;
543
 
543
 
544
		$resultat = array();
544
		$resultat = array();
545
		foreach($resultat_sql as $v) $resultat[$v['id_mot_cle_obs']] = $v['mot_cle'];
545
		foreach($resultat_sql as $v) $resultat[$v['id_mot_cle_obs']] = $v['mot_cle'];
546
 
546
 
547
		$enregistrement['mots_cles_texte'] = implode(',', $liste_mots_cle);
547
		$enregistrement['mots_cles_texte'] = implode(',', $liste_mots_cle);
548
		$enregistrement['_mots_cle'] = array("existing" => $resultat,
548
		$enregistrement['_mots_cle'] = array("existing" => $resultat,
549
											 "to_insert" => array_diff($liste_mots_cle, $resultat));
549
											 "to_insert" => array_diff($liste_mots_cle, $resultat));
550
	}
550
	}
551
 
551
 
552
 
552
 
553
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
553
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
554
 
554
 
555
	// TODO: PHP 5.3, utiliser date_parse_from_format()
555
	// TODO: PHP 5.3, utiliser date_parse_from_format()
556
	// TODO: parser les heures (cf product-owner)
556
	// TODO: parser les heures (cf product-owner)
557
	// TODO: passer par le timestamp pour s'assurer de la validité
557
	// TODO: passer par le timestamp pour s'assurer de la validité
558
	static function traiterDateObs($date, $ligne) {
558
	static function traiterDateObs($date, $ligne) {
559
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
559
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
560
		if(is_double($date)) {
560
		if(is_double($date)) {
561
			if($date > 0)
561
			if($date > 0)
562
				return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
562
				return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
563
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
563
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
564
						  "Attention: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble",
564
						  "Attention: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble",
565
						  E_USER_NOTICE);
565
						  E_USER_NOTICE);
566
			
566
			
567
			// throw new Exception("erreur: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble");
567
			// throw new Exception("erreur: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble");
568
 
568
 
569
			// attention, UNIX timestamp, car Excel les décompte depuis 1900
569
			// attention, UNIX timestamp, car Excel les décompte depuis 1900
570
			// cf http://fczaja.blogspot.fr/2011/06/convert-excel-date-into-timestamp.html
570
			// cf http://fczaja.blogspot.fr/2011/06/convert-excel-date-into-timestamp.html
571
			// $timestamp = ($date - MIN_DATES_DIFF) * 60 * 60 * 24 - time(); // NON
571
			// $timestamp = ($date - MIN_DATES_DIFF) * 60 * 60 * 24 - time(); // NON
572
 
572
 
573
			// $timestamp = PHPExcel_Calculation::getInstance()->calculateFormula("=" . $date . "-DATE(1970,1,1)*60*60*24"); // NON
573
			// $timestamp = PHPExcel_Calculation::getInstance()->calculateFormula("=" . $date . "-DATE(1970,1,1)*60*60*24"); // NON
574
 
574
 
575
			// echo strftime("%Y/%m/%d 00:00:00", $timestamp); // NON
575
			// echo strftime("%Y/%m/%d 00:00:00", $timestamp); // NON
576
		}
576
		}
577
		else {
577
		else {
578
			$timestamp = strtotime($date);
578
			$timestamp = strtotime($date);
579
			if(! $timestamp) {
579
			if(! $timestamp) {
580
				if($date) trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": Attention: date erronée ($date)", E_USER_NOTICE);
580
				if($date) trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": Attention: date erronée ($date)", E_USER_NOTICE);
581
				return NULL;
581
				return NULL;
582
			}
582
			}
583
			return strftime("%Y-%m-%d 00:00:00", strtotime($date));
583
			return strftime("%Y-%m-%d 00:00:00", strtotime($date));
584
		}
584
		}
585
	}
585
	}
586
 
586
 
587
	static function identReferentiel($referentiel) {
587
	static function identReferentiel($referentiel) {
588
		// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
588
		// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
589
		if(strpos(strtolower($referentiel), 'bdtfx') !== FALSE) return 'bdtfx:v1.01';
589
		if(strpos(strtolower($referentiel), 'bdtfx') !== FALSE) return 'bdtfx:v1.01';
590
		if(strpos(strtolower($referentiel), 'bdtxa') !== FALSE) return 'bdtxa:v1.00';
590
		if(strpos(strtolower($referentiel), 'bdtxa') !== FALSE) return 'bdtxa:v1.00';
591
		if(strpos(strtolower($referentiel), 'bdnff') !== FALSE) return 'bdnff:4.02';
591
		if(strpos(strtolower($referentiel), 'bdnff') !== FALSE) return 'bdnff:4.02';
592
		if(strpos(strtolower($referentiel), 'isfan') !== FALSE) return 'isfan:v1.00';
592
		if(strpos(strtolower($referentiel), 'isfan') !== FALSE) return 'isfan:v1.00';
593
 
593
 
594
		if($referentiel) {
594
		if($referentiel) {
595
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": Attention: référentiel inconnu", E_USER_NOTICE);
595
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": Attention: référentiel inconnu", E_USER_NOTICE);
596
		}
596
		}
597
		return NULL;
597
		return NULL;
598
		/* TODO: cf story,
598
		/* TODO: cf story,
599
		   En cas de NULL faire une seconde passe de détection à partir du nom saisi
599
		   En cas de NULL faire une seconde passe de détection à partir du nom saisi
600
		   + accepter les n° de version */
600
		   + accepter les n° de version */
601
	}
601
	}
602
 
602
 
603
	static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx:v1.01', $ligne) {
603
	static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx:v1.01', $ligne) {
604
		// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
604
		// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
605
		if($lon && is_string($lon)) $lon = str_replace(',', '.', $lon);
605
		if($lon && is_string($lon)) $lon = str_replace(',', '.', $lon);
606
		if($lat && is_string($lat)) $lat = str_replace(',', '.', $lat);
606
		if($lat && is_string($lat)) $lat = str_replace(',', '.', $lat);
607
 
607
 
608
		// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
608
		// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
609
		// tout en uniformisant le format de séparateur des décimales (le ".")
609
		// tout en uniformisant le format de séparateur des décimales (le ".")
610
		if($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) return sprintf('%.5F', $lon);
610
		if($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) return sprintf('%.5F', $lon);
611
		if($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) return sprintf('%.5F', $lat);
611
		if($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) return sprintf('%.5F', $lat);
612
 
612
 
613
		if($lon || $lat) {
613
		if($lon || $lat) {
614
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
614
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
615
						  "Attention: longitude ou latitude erronée",
615
						  "Attention: longitude ou latitude erronée",
616
						  E_USER_NOTICE);
616
						  E_USER_NOTICE);
617
		}
617
		}
618
		return NULL;
618
		return NULL;
619
 
619
 
620
		/* limite france métropole si bdtfx ? ou bdtxa ? ...
620
		/* limite france métropole si bdtfx ? ou bdtxa ? ...
621
		   NON!
621
		   NON!
622
		   Un taxon d'un référentiel donné peut être théoriquement observé n'importe où sur le globe.
622
		   Un taxon d'un référentiel donné peut être théoriquement observé n'importe où sur le globe.
623
		   Il n'y a pas lieu d'effectuer des restriction ici.
623
		   Il n'y a pas lieu d'effectuer des restriction ici.
624
		   Cependant des erreurs fréquentes (0,0 ou lon/lat inversées) peuvent être détectés ici.
624
		   Cependant des erreurs fréquentes (0,0 ou lon/lat inversées) peuvent être détectés ici.
625
		   TODO */
625
		   TODO */
626
		$bbox = self::getReferentielBBox($referentiel);
626
		$bbox = self::getReferentielBBox($referentiel);
627
		if(!$bbox) return NULL;
627
		if(!$bbox) return NULL;
628
 
628
 
629
		if($lon) {
629
		if($lon) {
630
			if($lon < $bbox['EST'] && $lon > $bbox['OUEST']) return is_numeric($lon) ? $lon : NULL;
630
			if($lon < $bbox['EST'] && $lon > $bbox['OUEST']) return is_numeric($lon) ? $lon : NULL;
631
			else return NULL;
631
			else return NULL;
632
		}
632
		}
633
		if($lat) {
633
		if($lat) {
634
			if($lat < $bbox['NORD'] && $lat > $bbox['SUD']) return is_numeric($lat) ? $lat : NULL;
634
			if($lat < $bbox['NORD'] && $lat > $bbox['SUD']) return is_numeric($lat) ? $lat : NULL;
635
			return NULL;
635
			return NULL;
636
		}
636
		}
637
	}
637
	}
638
 
638
 
639
 
639
 
640
	static function traiterEspece($ligne, Array &$espece, $cel) {
640
	static function traiterEspece($ligne, Array &$espece, $cel) {
641
		if(!$ligne[C_NOM_SEL]) return;
641
		if(!$ligne[C_NOM_SEL]) return;
642
 
642
 
643
		// nom_sel reste toujours celui de l'utilisateur
643
		// nom_sel reste toujours celui de l'utilisateur
644
		$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
644
		$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
645
 
645
 
646
		$taxon_info_webservice = new RechercheInfosTaxonBeta($cel->config);
646
		$taxon_info_webservice = new RechercheInfosTaxonBeta($cel->config);
647
 
647
 
648
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
648
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
649
 
649
 
650
		// TODO: si empty(C_NOM_SEL) et !empty(C_NOM_SEL_NN) : recherche info à partir de C_NOM_SEL_NN
650
		// TODO: si empty(C_NOM_SEL) et !empty(C_NOM_SEL_NN) : recherche info à partir de C_NOM_SEL_NN
651
		echo "rechercherInformationsComplementairesSurNom()\n";
651
		echo "rechercherInformationsComplementairesSurNom()\n";
652
		/*
652
		/*
653
		  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
653
		  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
654
		  #
654
		  #
655
		  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
655
		  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
656
		  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
656
		  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
657
		  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
657
		  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
658
		  #
658
		  #
659
 
659
 
660
		  SELECT nom_sci, num_nom_retenu, nom_sci_html, auteur, annee, biblio_origine FROM bdtfx_v1_01 WHERE num_nom = 31468
660
		  SELECT nom_sci, num_nom_retenu, nom_sci_html, auteur, annee, biblio_origine FROM bdtfx_v1_01 WHERE num_nom = 31468
661
		*/
661
		*/
662
		$resultat_recherche_espece = $taxon_info_webservice->rechercherInformationsComplementairesSurNom($ligne[C_NOM_SEL]);
662
		$resultat_recherche_espece = $taxon_info_webservice->rechercherInformationsComplementairesSurNom($ligne[C_NOM_SEL]);
663
 
663
 
664
		// on supprime les noms retenus et renvoi tel quel
664
		// on supprime les noms retenus et renvoi tel quel
665
		// on réutilise les define pour les noms d'indexes, tant qu'à faire
665
		// on réutilise les define pour les noms d'indexes, tant qu'à faire
666
		if (! $resultat_recherche_espece) {
666
		if (! $resultat_recherche_espece) {
667
			// XXX; tout à NULL sauf C_NOM_SEL ci-dessus ?
667
			// XXX; tout à NULL sauf C_NOM_SEL ci-dessus ?
668
			$espece[C_NOM_SEL_NN] = $ligne[C_NOM_SEL_NN];
668
			$espece[C_NOM_SEL_NN] = $ligne[C_NOM_SEL_NN];
669
			$espece[C_NOM_RET] = $ligne[C_NOM_RET];
669
			$espece[C_NOM_RET] = $ligne[C_NOM_RET];
670
			$espece[C_NOM_RET_NN] = $ligne[C_NOM_RET_NN];
670
			$espece[C_NOM_RET_NN] = $ligne[C_NOM_RET_NN];
671
			$espece[C_NT] = $ligne[C_NT];
671
			$espece[C_NT] = $ligne[C_NT];
672
			$espece[C_FAMILLE] = $ligne[C_FAMILLE];
672
			$espece[C_FAMILLE] = $ligne[C_FAMILLE];
673
 
673
 
674
			return;
674
			return;
675
		}
675
		}
676
 
676
 
677
		// succès de la détection = écrasement du numéro nomenclatural saisi...
677
		// succès de la détection = écrasement du numéro nomenclatural saisi...
678
		$espece[C_NOM_SEL_NN] = $resultat_recherche_espece[0][0];
678
		$espece[C_NOM_SEL_NN] = $resultat_recherche_espece[0][0];
679
		// et des info complémentaires
679
		// et des info complémentaires
680
		echo "rechercherInformationsComplementairesSurNumNom()\n";
680
		echo "rechercherInformationsComplementairesSurNumNom()\n";
681
		$complement = $taxon_info_webservice->rechercherInformationsComplementairesSurNumNom($resultat_recherche_espece[0][0]);
681
		$complement = $taxon_info_webservice->rechercherInformationsComplementairesSurNumNom($resultat_recherche_espece[0][0]);
682
		/*
682
		/*
683
		  // GET /service:eflore:0.1/bdtfx/noms/31468?retour.champs=nom_sci,auteur,id,nom_retenu_complet,nom_retenu.id,num_taxonomique,famille
683
		  // GET /service:eflore:0.1/bdtfx/noms/31468?retour.champs=nom_sci,auteur,id,nom_retenu_complet,nom_retenu.id,num_taxonomique,famille
684
		  /home/raphael/eflore/projets/services/modules/0.1/bdtfx/Noms.php:280
684
		  /home/raphael/eflore/projets/services/modules/0.1/bdtfx/Noms.php:280
685
		  SELECT  *, nom_sci   FROM bdtfx_v1_01  WHERE num_nom = '31468' 
685
		  SELECT  *, nom_sci   FROM bdtfx_v1_01  WHERE num_nom = '31468' 
686
		  SELECT nom_sci, num_nom_retenu, nom_sci_html, auteur, annee, biblio_origine FROM bdtfx_v1_01 WHERE num_nom = 31468
686
		  SELECT nom_sci, num_nom_retenu, nom_sci_html, auteur, annee, biblio_origine FROM bdtfx_v1_01 WHERE num_nom = 31468
687
		  SELECT nom_sci, num_nom_retenu, nom_sci_html, auteur, annee, biblio_origine FROM bdtfx_v1_01 WHERE num_nom = 86535
687
		  SELECT nom_sci, num_nom_retenu, nom_sci_html, auteur, annee, biblio_origine FROM bdtfx_v1_01 WHERE num_nom = 86535
688
		*/
688
		*/
689
		$espece[C_NOM_RET] = $complement['Nom_Retenu'];
689
		$espece[C_NOM_RET] = $complement['Nom_Retenu'];
690
		$espece[C_NOM_RET_NN] = $complement['Num_Nom_Retenu'];
690
		$espece[C_NOM_RET_NN] = $complement['Num_Nom_Retenu'];
691
		$espece[C_NT] = $complement['Num_Taxon'];
691
		$espece[C_NT] = $complement['Num_Taxon'];
692
		$espece[C_FAMILLE] = $complement['Famille'];
692
		$espece[C_FAMILLE] = $complement['Famille'];
693
		var_dump("a", $espece);die;
693
		var_dump("a", $espece);die;
694
	}
694
	}
695
 
695
 
696
	static function detectFromNom($nom, $cel) {
696
	static function detectFromNom($nom, $cel) {
697
		$r = $cel->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s') ".
697
		$r = $cel->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s') ".
698
									"ORDER BY nom_sci ASC LIMIT 0, 1",
698
									"ORDER BY nom_sci ASC LIMIT 0, 1",
699
									$cel->proteger($nom)));
699
									$cel->proteger($nom)));
700
		if($r) return $r;
700
		if($r) return $r;
701
 
701
 
702
		$cel->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s' OR nom LIKE '%s') ".
702
		$cel->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s' OR nom LIKE '%s') ".
703
							   "ORDER BY nom_sci ASC LIMIT 0, 1",
703
							   "ORDER BY nom_sci ASC LIMIT 0, 1",
704
							   $cel->proteger($nom),
704
							   $cel->proteger($nom),
705
							   $cel->proteger(str_replace(' ', '% ', $nom))));
705
							   $cel->proteger(str_replace(' ', '% ', $nom))));
706
		return $r;
706
		return $r;
707
	}
707
	}
-
 
708
 
-
 
709
 
-
 
710
	static function traiterLocalisation($ligne, Array &$localisation, $cel) {
-
 
711
	    $identifiant_commune = trim($ligne[C_ZONE_GEO]);
-
 
712
		if(!$identifiant_commune) {
-
 
713
			$departement = trim($ligne[C_CE_ZONE_GEO]);
-
 
714
			if(strpos($departement, "INSEE-C:", 0) === 0) {
-
 
715
				$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
-
 
716
				$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
-
 
717
			}
-
 
718
 
-
 
719
 
-
 
720
			if(!is_numeric($departement)) {
-
 
721
				$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
-
 
722
				$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
-
 
723
			}
-
 
724
 
-
 
725
			if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
-
 
726
			if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
-
 
727
			// if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
-
 
728
 
-
 
729
			$departement = trim($departement); // TODO
-
 
730
 
-
 
731
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
-
 
732
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
-
 
733
 
-
 
734
		}
-
 
735
 
-
 
736
 
-
 
737
		$select = "SELECT DISTINCT nom, code  FROM cel_zones_geo";
-
 
738
	
-
 
739
		if (preg_match('/(.*) \((\d+)\)/', $identifiant_commune, $elements)) {
-
 
740
			// commune + departement : montpellier (34)
-
 
741
			$nom_commune=$elements[1];
-
 
742
			$code_commune=$elements[2];
-
 
743
	 	    $requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
-
 
744
							   $select, $cel->quoteNonNull($nom_commune), $cel->quoteNonNull($code_commune.'%'));
-
 
745
		}
-
 
746
		elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
-
 
747
			// Code insee seul  
-
 
748
			$code_insee_commune=$elements[1];
-
 
749
	 	    $requete = sprintf("%s WHERE code = %s", $select, $cel->quoteNonNull($code_insee_commune));
-
 
750
		}
-
 
751
		else {
-
 
752
			// Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
-
 
753
			// on prend le risque ici de retourner une mauvaise Commune
-
 
754
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
-
 
755
	 	    $requete = sprintf("%s WHERE nom LIKE %s", $select, $cel->quoteNonNull($nom_commune.'%'));
-
 
756
		}
-
 
757
	
-
 
758
		$resultat_commune = $cel->requeter($requete);
-
 
759
		// TODO: levenstein sort ?
-
 
760
 
-
 
761
		// cas de la commune introuvable dans le référentiel
-
 
762
		// réinitialisation aux valeurs du fichier XLS
-
 
763
		if(! $resultat_commune) {
-
 
764
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
-
 
765
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
-
 
766
		} else {
-
 
767
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
-
 
768
			$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
-
 
769
		}
-
 
770
 
-
 
771
		$departement = &$localisation[C_CE_ZONE_GEO];
-
 
772
 
-
 
773
		if(strpos($departement, "INSEE-C:", 0) === 0) {
-
 
774
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
-
 
775
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
-
 
776
		}
-
 
777
 
-
 
778
 
-
 
779
		if(!is_numeric($departement)) {
-
 
780
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
-
 
781
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
-
 
782
		}
-
 
783
 
-
 
784
		if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
-
 
785
		if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
-
 
786
		// if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
-
 
787
 
-
 
788
		$departement = trim($departement); // TODO
-
 
789
 
-
 
790
		$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
-
 
791
		$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
-
 
792
	}
-
 
793
 
708
 
794
	/*
709
	static function traiterLocalisation($ligne, Array &$localisation, $cel) {
795
	static function traiterLocalisation($ligne, Array &$localisation, $cel) {
710
	    $identifiant_commune = trim($ligne[C_ZONE_GEO]);
796
	    $identifiant_commune = trim($ligne[C_ZONE_GEO]);
711
		if(!$identifiant_commune) {
797
		if(!$identifiant_commune) {
712
			$departement = trim($ligne[C_CE_ZONE_GEO]);
798
			$departement = trim($ligne[C_CE_ZONE_GEO]);
713
			goto testdepartement;
799
			goto testdepartement;
714
		}
800
		}
715
 
801
 
716
 
802
 
717
		$select = "SELECT DISTINCT nom, code  FROM cel_zones_geo";
803
		$select = "SELECT DISTINCT nom, code  FROM cel_zones_geo";
718
	
804
	
719
		if (preg_match('/(.*) \((\d+)\)/', $identifiant_commune, $elements)) {
805
		if (preg_match('/(.*) \((\d+)\)/', $identifiant_commune, $elements)) {
720
			// commune + departement : montpellier (34)
806
			// commune + departement : montpellier (34)
721
			$nom_commune=$elements[1];
807
			$nom_commune=$elements[1];
722
			$code_commune=$elements[2];
808
			$code_commune=$elements[2];
723
	 	    $requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
809
	 	    $requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
724
							   $select, $cel->quoteNonNull($nom_commune), $cel->quoteNonNull($code_commune.'%'));
810
							   $select, $cel->quoteNonNull($nom_commune), $cel->quoteNonNull($code_commune.'%'));
725
		}
811
		}
726
		elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
812
		elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
727
			// Code insee seul  
813
			// Code insee seul  
728
			$code_insee_commune=$elements[1];
814
			$code_insee_commune=$elements[1];
729
	 	    $requete = sprintf("%s WHERE code = %s", $select, $cel->quoteNonNull($code_insee_commune));
815
	 	    $requete = sprintf("%s WHERE code = %s", $select, $cel->quoteNonNull($code_insee_commune));
730
		}
816
		}
731
		else {
817
		else {
732
			// Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
818
			// Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
733
			// on prend le risque ici de retourner une mauvaise Commune
819
			// on prend le risque ici de retourner une mauvaise Commune
734
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
820
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
735
	 	    $requete = sprintf("%s WHERE nom LIKE %s", $select, $cel->quoteNonNull($nom_commune.'%'));
821
	 	    $requete = sprintf("%s WHERE nom LIKE %s", $select, $cel->quoteNonNull($nom_commune.'%'));
736
		}
822
		}
737
	
823
	
738
		$resultat_commune = $cel->requeter($requete);
824
		$resultat_commune = $cel->requeter($requete);
739
		// TODO: levenstein sort ?
825
		// TODO: levenstein sort ?
740
 
826
 
741
		// cas de la commune introuvable dans le référentiel
827
		// cas de la commune introuvable dans le référentiel
742
		// réinitialisation aux valeurs du fichier XLS
828
		// réinitialisation aux valeurs du fichier XLS
743
		if(! $resultat_commune) {
829
		if(! $resultat_commune) {
744
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
830
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
745
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
831
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
746
		} else {
832
		} else {
747
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
833
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
748
			$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
834
			$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
749
		}
835
		}
750
 
836
 
751
		$departement = &$localisation[C_CE_ZONE_GEO];
837
		$departement = &$localisation[C_CE_ZONE_GEO];
752
 
838
 
753
	testdepartement:
839
	testdepartement:
754
		if(strpos($departement, "INSEE-C:", 0) === 0) goto protectloc;
840
		if(strpos($departement, "INSEE-C:", 0) === 0) goto protectloc;
755
 
841
 
756
		if(!is_numeric($departement)) goto protectloc; // TODO ?
842
		if(!is_numeric($departement)) goto protectloc; // TODO ?
757
		if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
843
		if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
758
		if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
844
		if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
759
		// if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
845
		// if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
760
 
846
 
761
		$departement = trim($departement); // TODO
847
		$departement = trim($departement); // TODO
762
 
848
 
763
	protectloc:
849
	protectloc:
764
		$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
850
		$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
765
		$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
851
		$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
766
	}
852
	}
767
 
853
*/
768
 
854
 
769
 
855
 
770
	/* HELPERS */
856
	/* HELPERS */
771
 
857
 
772
	// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
858
	// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
773
	static function sortArrayByArray($array, $orderArray) {
859
	static function sortArrayByArray($array, $orderArray) {
774
		$ordered = array();
860
		$ordered = array();
775
		foreach($orderArray as $key) {
861
		foreach($orderArray as $key) {
776
			if(array_key_exists($key, $array)) {
862
			if(array_key_exists($key, $array)) {
777
				$ordered[$key] = $array[$key];
863
				$ordered[$key] = $array[$key];
778
				unset($array[$key]);
864
				unset($array[$key]);
779
			}
865
			}
780
		}
866
		}
781
		return $ordered + $array;
867
		return $ordered + $array;
782
	}
868
	}
783
 
869
 
784
	// retourne une BBox [N,S,E,O) pour un référentiel donné
870
	// retourne une BBox [N,S,E,O) pour un référentiel donné
785
	static function getReferentielBBox($referentiel) {
871
	static function getReferentielBBox($referentiel) {
786
		if($referentiel == 'bdtfx:v1.01') return Array(
872
		if($referentiel == 'bdtfx:v1.01') return Array(
787
			'NORD' => 51.2, // Dunkerque
873
			'NORD' => 51.2, // Dunkerque
788
			'SUD' => 41.3, // Bonifacio
874
			'SUD' => 41.3, // Bonifacio
789
			'EST' => 9.7, // Corse
875
			'EST' => 9.7, // Corse
790
			'OUEST' => -5.2); // Ouessan
876
			'OUEST' => -5.2); // Ouessan
791
		return FALSE;
877
		return FALSE;
792
	}
878
	}
793
 
879
 
794
	// ces valeurs ne sont pas inséré via les placeholders du PDO::preparedStatement
880
	// ces valeurs ne sont pas inséré via les placeholders du PDO::preparedStatement
795
	// et doivent donc être échappées correctement.
881
	// et doivent donc être échappées correctement.
796
	public function initialiser_colonnes_statiques() {
882
	public function initialiser_colonnes_statiques() {
797
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
883
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
798
												Array(
884
												Array(
799
													"ce_utilisateur" => $this->id_utilisateur,
885
													"ce_utilisateur" => $this->id_utilisateur,
800
													"prenom_utilisateur" => $this->quoteNonNull($this->utilisateur['prenom']),
886
													"prenom_utilisateur" => $this->quoteNonNull($this->utilisateur['prenom']),
801
													"nom_utilisateur" => $this->quoteNonNull($this->utilisateur['nom']),
887
													"nom_utilisateur" => $this->quoteNonNull($this->utilisateur['nom']),
802
													"courriel_utilisateur" => $this->quoteNonNull($this->utilisateur['courriel']),
888
													"courriel_utilisateur" => $this->quoteNonNull($this->utilisateur['courriel']),
803
												));
889
												));
804
 
890
 
805
	}
891
	}
806
 
892
 
807
	static function initialiser_pdo_ordered_statements($colonnes_statiques) {
893
	static function initialiser_pdo_ordered_statements($colonnes_statiques) {
808
		return Array(
894
		return Array(
809
			// insert_ligne_pattern_ordre
895
			// insert_ligne_pattern_ordre
810
			sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
896
			sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
811
					implode(', ', array_keys($colonnes_statiques)),
897
					implode(', ', array_keys($colonnes_statiques)),
812
					implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
898
					implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
813
 
899
 
814
			// insert_ligne_pattern_ordre
900
			// insert_ligne_pattern_ordre
815
			sprintf('(%s, %s ?)',
901
			sprintf('(%s, %s ?)',
816
					implode(', ', $colonnes_statiques),
902
					implode(', ', $colonnes_statiques),
817
					str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
903
					str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
818
		);
904
		);
819
	}
905
	}
820
 
906
 
821
	static function initialiser_pdo_statements($colonnes_statiques) {
907
	static function initialiser_pdo_statements($colonnes_statiques) {
822
		return Array( 
908
		return Array( 
823
			// insert_prefix
909
			// insert_prefix
824
			sprintf('INSERT INTO cel_obs (%s) VALUES ',
910
			sprintf('INSERT INTO cel_obs (%s) VALUES ',
825
					implode(', ', self::$ordre_BDD)),
911
					implode(', ', self::$ordre_BDD)),
826
 
912
 
827
 
913
 
828
			// insert_ligne_pattern, cf: self::$insert_ligne_pattern
914
			// insert_ligne_pattern, cf: self::$insert_ligne_pattern
829
			'(' .
915
			'(' .
830
			// 3) créé une chaîne de liste de champ à inséré en DB
916
			// 3) créé une chaîne de liste de champ à inséré en DB
831
			implode(', ', array_values(
917
			implode(', ', array_values(
832
				// 2) garde les valeurs fixes (de $colonnes_statiques),
918
				// 2) garde les valeurs fixes (de $colonnes_statiques),
833
				// mais remplace les NULL par des "?"
919
				// mais remplace les NULL par des "?"
834
				array_map('__anonyme_5',
920
				array_map('__anonyme_5',
835
						  // 1) créé un tableau genre (nom_sel_nn => NULL) depuis self::$ordre_BDD
921
						  // 1) créé un tableau genre (nom_sel_nn => NULL) depuis self::$ordre_BDD
836
						  // et écrase certaines valeurs avec $colonnes_statiques (initilisé avec les données utilisateur)
922
						  // et écrase certaines valeurs avec $colonnes_statiques (initilisé avec les données utilisateur)
837
						  array_merge(array_map('__anonyme_6', array_flip(self::$ordre_BDD)), $colonnes_statiques
923
						  array_merge(array_map('__anonyme_6', array_flip(self::$ordre_BDD)), $colonnes_statiques
838
				)))) .
924
				)))) .
839
			')'
925
			')'
840
		);
926
		);
841
	}
927
	}
842
 
928
 
843
	// équivalent à CEL->Bdd->proteger() (qui wrap PDO::quote),
929
	// équivalent à CEL->Bdd->proteger() (qui wrap PDO::quote),
844
	// sans transformer NULL en ""
930
	// sans transformer NULL en ""
845
	private function quoteNonNull($chaine) {
931
	private function quoteNonNull($chaine) {
846
		if(is_null($chaine)) return "NULL";
932
		if(is_null($chaine)) return "NULL";
847
		if(!is_string($chaine)) die("erreur __FILE__, __LINE__");
933
		if(!is_string($chaine)) die("erreur __FILE__, __LINE__");
848
		return $this->bdd->quote($chaine);
934
		return $this->bdd->quote($chaine);
849
	}
935
	}
850
 
936
 
851
	public function erreurs_stock($errno, $errstr) {
937
	public function erreurs_stock($errno, $errstr) {
852
		$this->bilan[] = $errstr;
938
		$this->bilan[] = $errstr;
853
	}
939
	}
854
}
940
}