Subversion Repositories eFlore/Applications.cel

Rev

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

Rev 1646 Rev 1648
1
<?php
1
<?php
2
 
2
 
3
/**
3
/**
4
* @category  PHP
4
* @category  PHP
5
* @package   jrest
5
* @package   jrest
6
* @author    Raphaël Droz <raphael@tela-botania.org>
6
* @author    Raphaël Droz <raphael@tela-botania.org>
7
* @copyright 2013 Tela-Botanica
7
* @copyright 2013 Tela-Botanica
8
* @license   http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
8
* @license   http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
9
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
9
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
10
*/
10
*/
11
 
11
 
12
/**
12
/**
13
 * Service d'import de données d'observation du CEL au format XLS
13
 * Service d'import de données d'observation du CEL au format XLS
14
 */
14
 */
15
 
15
 
16
// sont define()'d commme n° de colonne tous les abbrevs retournés par ExportXLS::nomEnsembleVersListeColonnes()
16
// sont define()'d commme n° de colonne tous les abbrevs retournés par ExportXLS::nomEnsembleVersListeColonnes()
17
// préfixés par C_  cf: detectionEntete()
17
// préfixés par C_  cf: detectionEntete()
18
 
18
 
19
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(realpath(__FILE__))) . '/lib');
19
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(realpath(__FILE__))) . '/lib');
20
// TERM
20
// TERM
21
error_reporting(-1);
21
error_reporting(-1);
22
ini_set('html_errors', 0);
22
ini_set('html_errors', 0);
23
ini_set('xdebug.cli_color', 2);
23
ini_set('xdebug.cli_color', 2);
24
require_once('lib/PHPExcel/Classes/PHPExcel.php');
24
require_once('lib/PHPExcel/Classes/PHPExcel.php');
25
require_once('ExportXLS.php');
25
require_once('ExportXLS.php');
26
 
26
 
27
 
27
 
28
date_default_timezone_set("Europe/Paris");
28
date_default_timezone_set("Europe/Paris");
29
 
29
 
30
// nombre d'INSERT à cumuler par requête SQL
30
// nombre d'INSERT à cumuler par requête SQL
31
// (= nombre de lignes XLS à bufferiser)
31
// (= nombre de lignes XLS à bufferiser)
32
define('NB_LIRE_LIGNE_SIMUL', 30);
32
//define('NB_LIRE_LIGNE_SIMUL', 30);
-
 
33
define('NB_LIRE_LIGNE_SIMUL', 5);
33
 
34
 
34
// Numbers of days between January 1, 1900 and 1970 (including 19 leap years)
35
// Numbers of days between January 1, 1900 and 1970 (including 19 leap years)
35
// see traiterDateObs()
36
// see traiterDateObs()
36
define("MIN_DATES_DIFF", 25569);
37
define("MIN_DATES_DIFF", 25569);
37
 
38
 
38
 
39
 
39
class MyReadFilter implements PHPExcel_Reader_IReadFilter {
40
class MyReadFilter implements PHPExcel_Reader_IReadFilter {
40
	// exclusion de colonnes
41
	// exclusion de colonnes
41
	public $exclues = array();
42
	public $exclues = array();
42
 
43
 
43
	// lecture par morceaux
44
	// lecture par morceaux
44
    public $ligne_debut = 0; 
45
    public $ligne_debut = 0; 
45
    public $ligne_fin = 0;
46
    public $ligne_fin = 0;
46
 
47
 
47
	public function __construct() {}
48
	public function __construct() {}
48
	public function def_interval($debut, $nb) {
49
	public function def_interval($debut, $nb) {
49
		$this->ligne_debut = $debut;
50
		$this->ligne_debut = $debut;
50
		$this->ligne_fin = $debut + $nb;
51
		$this->ligne_fin = $debut + $nb;
51
	}
52
	}
52
    public function readCell($colonne, $ligne, $worksheetName = '') {
53
    public function readCell($colonne, $ligne, $worksheetName = '') {
53
		if(@$this->exclues[$colonne]) return false;
54
		if(@$this->exclues[$colonne]) return false;
54
		// si des n° de morceaux ont été initialisés, on filtre...
55
		// si des n° de morceaux ont été initialisés, on filtre...
55
		if($this->ligne_debut && ($ligne < $this->ligne_debut || $ligne >= $this->ligne_fin)) return false;
56
		if($this->ligne_debut && ($ligne < $this->ligne_debut || $ligne >= $this->ligne_fin)) return false;
56
		return true;
57
		return true;
57
    } 
58
    } 
58
} 
59
} 
59
 
60
 
60
class ImportXLS extends Cel  {
61
class ImportXLS extends Cel  {
61
 
62
 
62
	static $ordre_BDD = Array(
63
	static $ordre_BDD = Array(
63
		"ce_utilisateur",
64
		"ce_utilisateur",
64
		"prenom_utilisateur",
65
		"prenom_utilisateur",
65
		"nom_utilisateur",
66
		"nom_utilisateur",
66
		"courriel_utilisateur",
67
		"courriel_utilisateur",
67
		"ordre",
68
		"ordre",
68
		"nom_sel",
69
		"nom_sel",
69
		"nom_sel_nn",
70
		"nom_sel_nn",
70
		"nom_ret",
71
		"nom_ret",
71
		"nom_ret_nn",
72
		"nom_ret_nn",
72
		"nt",
73
		"nt",
73
		"famille",
74
		"famille",
74
		"nom_referentiel",
75
		"nom_referentiel",
75
		"zone_geo",
76
		"zone_geo",
76
		"ce_zone_geo",
77
		"ce_zone_geo",
77
		"date_observation",
78
		"date_observation",
78
		"lieudit",
79
		"lieudit",
79
		"station",
80
		"station",
80
		"milieu",
81
		"milieu",
81
		"commentaire",
82
		"commentaire",
82
		"transmission",
83
		"transmission",
83
		"date_creation",
84
		"date_creation",
84
		"date_modification",
85
		"date_modification",
85
		"latitude",
86
		"latitude",
86
		"longitude");
87
		"longitude",
-
 
88
		"phenologie",
-
 
89
		"code_insee_calcule"
-
 
90
	);
-
 
91
 
-
 
92
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., phenologie, code_insee_calcule) VALUES"
-
 
93
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
-
 
94
	static $insert_prefix_ordre;
-
 
95
 
-
 
96
	// seconde possibilité
-
 
97
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., date_creation, ...phenologie, code_insee_calcule) VALUES"
-
 
98
	static $insert_prefix;
-
 
99
 
-
 
100
	// eg: "(<id>, <prenom>, <nom>, <email>, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
-
 
101
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
-
 
102
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
-
 
103
	static $insert_ligne_pattern_ordre;
-
 
104
 
-
 
105
	// seconde possibilité:
-
 
106
	// eg: "(<id>, <prenom>, <nom>, <email>, ?, ?, ?, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
-
 
107
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
-
 
108
	static $insert_ligne_pattern;
87
 
109
 
88
	/*
110
	/*
89
	  Ces colonnes:
111
	  Ces colonnes:
90
	  - sont propres à l'ensemble des enregistrements uploadés
112
	  - sont propres à l'ensemble des enregistrements uploadés
91
	  - sont indépendantes du numéro de lignes
113
	  - sont indépendantes du numéro de lignes
92
	  - n'ont pas de valeur par défaut dans la structure de la table
114
	  - n'ont pas de valeur par défaut dans la structure de la table
93
	  - nécessitent une initialisation dans le cadre de l'upload
115
	  - nécessitent une initialisation dans le cadre de l'upload
94
	*/
116
	*/
95
	public $colonnes_statiques = Array(
117
	public $colonnes_statiques = Array(
96
		"ce_utilisateur" => NULL,
118
		"ce_utilisateur" => NULL,
97
		"prenom_utilisateur" => NULL,
119
		"prenom_utilisateur" => NULL,
98
		"nom_utilisateur" => NULL,
120
		"nom_utilisateur" => NULL,
99
		"courriel_utilisateur" => NULL,
121
		"courriel_utilisateur" => NULL,
100
 
122
 
101
		// fixes (fonction SQL)
123
		// fixes (fonction SQL)
102
		// XXX future: mais pourraient varier dans le futur si la mise-à-jour
124
		// XXX future: mais pourraient varier dans le futur si la mise-à-jour
103
		// d'observation est implémentée
125
		// d'observation est implémentée
104
		"date_creation" => "now()",
126
		"date_creation" => "now()",
105
		"date_modification" => "now()",
127
		"date_modification" => "now()",
106
	);
128
	);
107
 
129
 
108
	public $id_utilisateur = NULL;
130
	public $id_utilisateur = NULL;
109
	// erreurs d'import
131
	// erreurs d'import
110
	public $bilan = Array();
132
	public $bilan = Array();
111
 
133
 
112
	function ExportXLS($config) {
134
	function ExportXLS($config) {
113
		parent::__construct($config);
135
		parent::__construct($config);
114
	}
136
	}
115
 
137
 
116
	function createElement($pairs) {
138
	function createElement($pairs) {
117
		if(!isset($pairs['utilisateur']) || trim($pairs['utilisateur']) == '') {
139
		if(!isset($pairs['utilisateur']) || trim($pairs['utilisateur']) == '') {
118
			echo '0'; exit;
140
			echo '0'; exit;
119
		}
141
		}
120
		$id_utilisateur = intval($pairs['utilisateur']);
142
		$id_utilisateur = intval($pairs['utilisateur']);
121
		$this->id_utilisateur = $id_utilisateur; // pour traiterImage();
143
		$this->id_utilisateur = $id_utilisateur; // pour traiterImage();
122
 
144
 
123
		if(!isset($_SESSION)) session_start();
145
		if(!isset($_SESSION)) session_start();
124
        $this->controleUtilisateur($id_utilisateur);
146
        $this->controleUtilisateur($id_utilisateur);
125
 
147
 
126
        $this->utilisateur = $this->getInfosComplementairesUtilisateur($id_utilisateur);
148
        $this->utilisateur = $this->getInfosComplementairesUtilisateur($id_utilisateur);
127
		$this->initialiser_colonnes_statiques($id_utilisateur);
149
		$this->initialiser_colonnes_statiques($id_utilisateur);
-
 
150
 
-
 
151
		// initialisation du statement PDO/MySQL
-
 
152
		self::$insert_prefix_ordre = 'INSERT INTO cel_obs ('.
-
 
153
			implode(', ', array_keys($this->colonnes_statiques)) .
-
 
154
			', ' .
-
 
155
			implode(', ', array_diff(self::$ordre_BDD, array_keys($this->colonnes_statiques))) .
-
 
156
			') VALUES ';
-
 
157
 
-
 
158
		self::$insert_prefix = 'INSERT INTO cel_obs ('.
-
 
159
			implode(', ', self::$ordre_BDD) . ') VALUES ';
-
 
160
 
-
 
161
		self::$insert_ligne_pattern_ordre =
-
 
162
			'(' .
-
 
163
			implode(', ', $this->colonnes_statiques) .
-
 
164
			', ' .
-
 
165
			str_repeat('?, ', count(self::$ordre_BDD) - count($this->colonnes_statiques) - 1) .
-
 
166
			'?' .
-
 
167
			')';
-
 
168
 
-
 
169
		self::$insert_ligne_pattern =
-
 
170
			'(' .
-
 
171
			implode(', ', array_values(array_map(
-
 
172
				function($item) { return is_null($item) ? '?' : $item; },
-
 
173
				array_merge(
-
 
174
					array_map(function() { return NULL; }, array_flip(self::$ordre_BDD)),
-
 
175
					$this->colonnes_statiques
-
 
176
				)))) .
128
 
177
			')';
129
 
178
 
130
		$infos_fichier = array_pop($_FILES);
179
		$infos_fichier = array_pop($_FILES);
131
		
180
		
132
		/*$objPHPExcel = PHPExcel_IOFactory::load($infos_fichier['tmp_name']);
181
		/*$objPHPExcel = PHPExcel_IOFactory::load($infos_fichier['tmp_name']);
133
		  $donnees = $objPHPExcel->getActiveSheet()->toArray(NULL,FALSE,FALSE,TRUE);*/
182
		  $donnees = $objPHPExcel->getActiveSheet()->toArray(NULL,FALSE,FALSE,TRUE);*/
134
 
183
 
135
		/*$objReader = PHPExcel_IOFactory::createReader("Excel5");
184
		/*$objReader = PHPExcel_IOFactory::createReader("Excel5");
136
		$objReader->setReadDataOnly(true);
185
		$objReader->setReadDataOnly(true);
137
		$objPHPExcel = $objReader->load($infos_fichier['tmp_name']);*/
186
		$objPHPExcel = $objReader->load($infos_fichier['tmp_name']);*/
138
 
187
 
139
		//var_dump($donnees);
188
		//var_dump($donnees);
140
 
189
 
141
		// renomme le fichier pour lui ajouter son extension initiale, ce qui
190
		// renomme le fichier pour lui ajouter son extension initiale, ce qui
142
		// permet (une sorte) d'autodétection du format.
191
		// permet (une sorte) d'autodétection du format.
143
		$fichier = $infos_fichier['tmp_name'];
192
		$fichier = $infos_fichier['tmp_name'];
144
		$extension = pathinfo($infos_fichier['name'], PATHINFO_EXTENSION);
193
		$extension = pathinfo($infos_fichier['name'], PATHINFO_EXTENSION);
145
		if( (strlen($extension) == 3 || strlen($extension) == 4) &&
194
		if( (strlen($extension) == 3 || strlen($extension) == 4) &&
146
			(rename($fichier, $fichier . '.' . $extension))) {
195
			(rename($fichier, $fichier . '.' . $extension))) {
147
			$fichier = $fichier . '.' . $extension;
196
			$fichier = $fichier . '.' . $extension;
148
		}
197
		}
149
 
198
 
150
		$objReader = PHPExcel_IOFactory::createReaderForFile($fichier);
199
		$objReader = PHPExcel_IOFactory::createReaderForFile($fichier);
151
		$objReader->setReadDataOnly(true);
200
		$objReader->setReadDataOnly(true);
152
 
201
 
153
		if(is_a($objReader, 'PHPExcel_Reader_CSV')) {
202
		if(is_a($objReader, 'PHPExcel_Reader_CSV')) {
154
			$objReader->setDelimiter(',')
203
			$objReader->setDelimiter(',')
155
				->setEnclosure('"')
204
				->setEnclosure('"')
156
				->setLineEnding("\n")
205
				->setLineEnding("\n")
157
				->setSheetIndex(0);
206
				->setSheetIndex(0);
158
		}
207
		}
159
 
208
 
160
		// on ne conserve que l'en-tête
209
		// on ne conserve que l'en-tête
161
		$filtre = new MyReadFilter();
210
		$filtre = new MyReadFilter();
162
		$filtre->def_interval(1, 2);
211
		$filtre->def_interval(1, 2);
163
		$objReader->setReadFilter($filtre);
212
		$objReader->setReadFilter($filtre);
164
 
213
 
165
		$objPHPExcel = $objReader->load($fichier);
214
		$objPHPExcel = $objReader->load($fichier);
166
		$obj_infos = $objReader->listWorksheetInfo($fichier);
215
		$obj_infos = $objReader->listWorksheetInfo($fichier);
167
		// XXX: indépendant du readFilter ?
216
		// XXX: indépendant du readFilter ?
168
		$nb_lignes = $obj_infos[0]['totalRows'];
217
		$nb_lignes = $obj_infos[0]['totalRows'];
169
 
218
 
170
		$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, FALSE, TRUE);
219
		$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, FALSE, TRUE);
171
		$filtre->exclues = self::detectionEntete($donnees[1]);
220
		$filtre->exclues = self::detectionEntete($donnees[1]);
172
 
221
 
173
		$obs_ajouts = 0;
222
		$obs_ajouts = 0;
174
		$obs_maj = 0;
223
		$obs_maj = 0;
175
		$dernier_ordre = $this->requeter("SELECT MAX(ordre) AS ordre FROM cel_obs WHERE ce_utilisateur = $id_utilisateur");
224
		$dernier_ordre = $this->requeter("SELECT MAX(ordre) AS ordre FROM cel_obs WHERE ce_utilisateur = $id_utilisateur");
176
		$dernier_ordre = intval($dernier_ordre[0]['ordre']) + 1;
225
		$dernier_ordre = intval($dernier_ordre[0]['ordre']) + 1;
177
		if(! $dernier_ordre) $dernier_ordre = 0;
226
		if(! $dernier_ordre) $dernier_ordre = 0;
178
 
227
 
179
		// on catch to les trigger_error(E_USER_NOTICE);
228
		// on catch to les trigger_error(E_USER_NOTICE);
180
		set_error_handler(array($this, 'erreurs_stock'), E_USER_NOTICE);
229
		set_error_handler(array($this, 'erreurs_stock'), E_USER_NOTICE);
181
 
230
 
182
		// lecture par morceaux (chunks), NB_LIRE_LIGNE_SIMUL lignes à fois
231
		// lecture par morceaux (chunks), NB_LIRE_LIGNE_SIMUL lignes à fois
183
		// pour aboutir des requêtes SQL d'insert groupés.
232
		// pour aboutir des requêtes SQL d'insert groupés.
184
		for($ligne = 2; $ligne < $nb_lignes + NB_LIRE_LIGNE_SIMUL; $ligne += NB_LIRE_LIGNE_SIMUL) {
233
		for($ligne = 2; $ligne < $nb_lignes + NB_LIRE_LIGNE_SIMUL; $ligne += NB_LIRE_LIGNE_SIMUL) {
185
			$filtre->def_interval($ligne, NB_LIRE_LIGNE_SIMUL);
234
			$filtre->def_interval($ligne, NB_LIRE_LIGNE_SIMUL);
186
			$objReader->setReadFilter($filtre);
235
			$objReader->setReadFilter($filtre);
187
 
236
 
188
			/* recharge avec $filtre actif (filtre sur lignes colonnes):
237
			/* recharge avec $filtre actif (filtre sur lignes colonnes):
189
			   - exclue les colonnes inutiles/inutilisables)
238
			   - exclue les colonnes inutiles/inutilisables)
190
			   - ne selectionne que les lignes dans le range [$ligne - $ligne + NB_LIRE_LIGNE_SIMUL] */
239
			   - ne selectionne que les lignes dans le range [$ligne - $ligne + NB_LIRE_LIGNE_SIMUL] */
191
			$objPHPExcel = $objReader->load($fichier);
240
			$objPHPExcel = $objReader->load($fichier);
192
			$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, FALSE, TRUE);
241
			$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, FALSE, TRUE);
193
 
242
 
194
			// ici on appel la fonction qui fera effectivement l'insertion multiple
243
			// ici on appel la fonction qui fera effectivement l'insertion multiple
195
			// à partir des (au plus) NB_LIRE_LIGNE_SIMUL lignes
244
			// à partir des (au plus) NB_LIRE_LIGNE_SIMUL lignes
196
 
245
 
197
			// TODO: passer $this, ne sert que pour appeler des méthodes public qui pourraient être statiques
246
			// TODO: passer $this, ne sert que pour appeler des méthodes public qui pourraient être statiques
198
			// notamment dans RechercheInfosTaxonBeta.php
247
			// notamment dans RechercheInfosTaxonBeta.php
199
			list($enregistrements, $images) =
248
			list($enregistrements, $images) =
200
				self::chargerLignes($this, $donnees, $this->colonnes_statiques, $dernier_ordre);
249
				self::chargerLignes($this, $donnees, $this->colonnes_statiques, $dernier_ordre);
201
			if(! $enregistrements) break;
250
			if(! $enregistrements) break;
-
 
251
 
-
 
252
			echo "===chunk\n";
-
 
253
			self::trierColonnes($enregistrements);
-
 
254
			// normalement: NB_LIRE_LIGNE_SIMUL, sauf si une enregistrement ne semble pas valide
-
 
255
			// ou bien lors du dernier chunk
-
 
256
 
-
 
257
			$nb_rec = count($enregistrements);
-
 
258
			$sql_pattern = self::$insert_prefix .
-
 
259
				str_repeat(self::$insert_ligne_pattern_ordre . ', ', $nb_rec - 1) .
-
 
260
				self::$insert_ligne_pattern_ordre;
-
 
261
 
-
 
262
			$sql_pattern = self::$insert_prefix .
-
 
263
				str_repeat(self::$insert_ligne_pattern . ', ', $nb_rec - 1) .
-
 
264
				self::$insert_ligne_pattern;
-
 
265
 
-
 
266
			$this->bdd->beginTransaction();
-
 
267
			$stmt = $this->bdd->prepare($sql_pattern);
-
 
268
			$donnees = array();
-
 
269
			foreach($enregistrements as $e) $donnees = array_merge($donnees, array_values($e));
-
 
270
 
-
 
271
			echo $sql_pattern;
-
 
272
			var_dump($enregistrements);
-
 
273
			var_dump($donnees);die;
-
 
274
 
-
 
275
			$stmt->execute($donnees);
-
 
276
 
-
 
277
			// $stmt->debugDumpParams(); // https://bugs.php.net/bug.php?id=52384
202
 
278
			//$this->bdd->commit();
-
 
279
			$dernier_autoinc = $this->bdd->lastInsertId();
-
 
280
			var_dump($dernier_autoinc);
-
 
281
			die;
-
 
282
 
-
 
283
 
203
			$dernier_autoinc = self::stockerEnregistrements($this, $enregistrements);
284
 
-
 
285
			$obs_ajouts += count($enregistrements);
204
			$obs_ajouts += count($enregistrements);
286
			// $obs_ajouts += count($enregistrements['insert']);
205
			// $obs_maj += count($enregistrements_a_MAJ);
287
			// $obs_maj += count($enregistrements['update']);
206
			self::stockerImages($this, $enregistrements, $images, $dernier_autoinc);
288
			self::stockerImages($this, $enregistrements, $images, $dernier_autoinc);
207
		}
289
		}
208
 
290
 
209
		restore_error_handler();
291
		restore_error_handler();
210
 
292
 
211
		if($this->bilan) echo implode("\n", $this->bilan) . "\n";
293
		if($this->bilan) echo implode("\n", $this->bilan) . "\n";
212
		// fin: renvoi summary
294
		// fin: renvoi summary
213
		die("$obs_ajouts observations ajoutées");
295
		die("$obs_ajouts observations ajoutées");
214
	}
296
	}
215
 
297
 
216
	static function detectionEntete($entete) {
298
	static function detectionEntete($entete) {
217
		$colonnes_reconnues = Array();
299
		$colonnes_reconnues = Array();
218
		$cols = ExportXLS::nomEnsembleVersListeColonnes('standard');
300
		$cols = ExportXLS::nomEnsembleVersListeColonnes('standard');
219
		foreach($entete as $k => $v) {
301
		foreach($entete as $k => $v) {
220
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
302
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
221
			foreach($cols as $col) {
303
			foreach($cols as $col) {
222
				$entete_officiel_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($col['nom'])));
304
				$entete_officiel_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($col['nom'])));
223
				$entete_officiel_abbrev = $col['abbrev'];
305
				$entete_officiel_abbrev = $col['abbrev'];
224
				if($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
306
				if($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
225
					//debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
307
					// debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
226
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
308
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
227
					$colonnes_reconnues[$k] = 1;
309
					$colonnes_reconnues[$k] = 1;
228
					break;
310
					break;
229
				}
311
				}
230
			}
312
			}
231
		}
313
		}
232
 
314
 
233
		// prépare le filtre de PHPExcel qui évitera le traitement de toutes les colonnes superflues
315
		// prépare le filtre de PHPExcel qui évitera le traitement de toutes les colonnes superflues
234
 
316
 
235
		// eg: diff ( Array( H => Commune, I => rien ) , Array( H => 1, K => 1 )
317
		// eg: diff ( Array( H => Commune, I => rien ) , Array( H => 1, K => 1 )
236
		// ==> Array( I => rien )
318
		// ==> Array( I => rien )
237
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
319
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
238
 
320
 
239
		// des colonnes de ExportXLS::nomEnsembleVersListeColonnes()
321
		// des colonnes de ExportXLS::nomEnsembleVersListeColonnes()
240
		// ne retient que celles marquées "importables"
322
		// ne retient que celles marquées "importables"
241
		$colonnes_automatiques = array_filter($cols, function($v) {	return !$v['importable']; });
323
		$colonnes_automatiques = array_filter($cols, function($v) {	return !$v['importable']; });
242
 
324
 
243
		// ne conserve que le nom long pour matcher avec la ligne XLS d'entête
325
		// ne conserve que le nom long pour matcher avec la ligne XLS d'entête
244
		array_walk($colonnes_automatiques, function(&$v) {	$v = $v['nom']; });
326
		array_walk($colonnes_automatiques, function(&$v) {	$v = $v['nom']; });
245
 
327
 
246
		// intersect ( Array ( N => Milieu, S => Ordre ), Array ( ordre => Ordre, phenologie => Phénologie ) )
328
		// intersect ( Array ( N => Milieu, S => Ordre ), Array ( ordre => Ordre, phenologie => Phénologie ) )
247
		// ==> Array ( S => Ordre, AA => Phénologie )
329
		// ==> Array ( S => Ordre, AA => Phénologie )
248
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
330
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
249
 
331
 
250
		// TODO: pourquoi ne pas comparer avec les abbrevs aussi ?
332
		// TODO: pourquoi ne pas comparer avec les abbrevs aussi ?
251
		// merge ( Array( I => rien ) , Array ( S => Ordre, AA => Phénologie ) )
333
		// merge ( Array( I => rien ) , Array ( S => Ordre, AA => Phénologie ) )
252
		// ==> Array ( I => rien, AA => Phénologie )
334
		// ==> Array ( I => rien, AA => Phénologie )
253
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
335
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
254
	}
336
	}
255
 
337
 
256
	/*
338
	/*
257
	 * charge un groupe de lignes
339
	 * charge un groupe de lignes
258
	 */
340
	 */
259
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
341
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
260
		$enregistrement = NULL;
342
		$enregistrement = NULL;
261
		$enregistrements = Array();
343
		$enregistrements = Array();
262
		$toutes_images = Array();
344
		$toutes_images = Array();
263
 
345
 
264
		foreach($lignes as $ligne) {
346
		foreach($lignes as $ligne) {
265
			//$ligne = array_filter($ligne, function($cell) { return !is_null($cell); });
347
			//$ligne = array_filter($ligne, function($cell) { return !is_null($cell); });
266
			//if(!$ligne) continue;
348
			//if(!$ligne) continue;
267
			// on a besoin des NULL pour éviter des notice d'index indéfini
349
			// on a besoin des NULL pour éviter des notice d'index indéfini
268
			if(! array_filter($ligne, function($cell) { return !is_null($cell); })) continue;
350
			if(! array_filter($ligne, function($cell) { return !is_null($cell); })) continue;
269
 
351
 
270
			if( ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) ) {
352
			if( ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) ) {
271
				$enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
353
				// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
-
 
354
				$enregistrements[] = $enregistrement;
272
 
355
 
273
				if(isset($enregistrement['_images'])) {
356
				if(isset($enregistrement['_images'])) {
274
					$pos = count($enregistrements) - 1;
357
					$pos = count($enregistrements) - 1;
275
					$last = &$enregistrements[$pos];
358
					$last = &$enregistrements[$pos];
276
					// ne dépend pas de cel_obs, et seront insérées *après* les enregistrements
359
					// ne dépend pas de cel_obs, et seront insérées *après* les enregistrements
277
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
360
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
278
					$toutes_images[] = Array("images" => $last['_images'],
361
					$toutes_images[] = Array("images" => $last['_images'],
279
											 "obs_pos" => $pos);
362
											 "obs_pos" => $pos);
280
					// ce champ n'a pas a faire partie de l'insertion dans cel_obs,
363
					// ce champ n'a pas a faire partie de l'insertion dans cel_obs,
281
					// mais est utile pour cel_obs_images
364
					// mais est utile pour cel_obs_images
282
					unset($last['_images']);
365
					unset($last['_images']);
283
				}
366
				}
284
 
367
 
285
				$dernier_ordre++;
368
				$dernier_ordre++;
286
			}
369
			}
287
		}
370
		}
288
 
371
 
289
		// XXX future: return Array($enregistrements_a_inserer, $enregistrements_a_MAJ, $toutes_images);
372
		// XXX future: return Array($enregistrements_a_inserer, $enregistrements_a_MAJ, $toutes_images);
290
		return Array($enregistrements, $toutes_images);
373
		return Array($enregistrements, $toutes_images);
291
	}
374
	}
292
 
375
 
293
 
376
 
294
	static function stockerEnregistrements($cel, $enregistrements) {
-
 
295
		$req = '';
-
 
296
 
377
	static function trierColonnes(&$enregistrements) {
297
		foreach($enregistrements as $enregistrement) {
378
		foreach($enregistrements as &$enregistrement) {
298
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
-
 
299
			array_walk($enregistrement,
379
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
300
					   function(&$item, $k, $obj) { $item = is_null($item) ? "NULL" : $item; },
-
 
301
					   $cel);
380
			//array_walk($enregistrement, function(&$item, $k) { $item = is_null($item) ? "NULL" : $item; });
302
			$req .= implode(', ', $enregistrement) . "\n";
381
			//$req .= implode(', ', $enregistrement) . "\n";
303
		}
-
 
304
		echo "$req\n";
-
 
305
		// TODO: insert
-
 
306
 
-
 
307
		// $cel->executer($req);
-
 
308
		// transactionnel + auto-inc
-
 
309
		return $cel->bdd->lastInsertId();
382
		}
310
	}
383
	}
311
 
384
 
312
 
385
 
313
	static function stockerImages($cel, $enregistrements, $toutes_images, $lastid) {
386
	static function stockerImages($cel, $enregistrements, $toutes_images, $lastid) {
314
		if(! $lastid) $lastid = rand();
387
		if(! $lastid) $lastid = rand();
315
 
388
 
316
		$images_insert =
389
		$images_insert =
317
			'INSERT INTO cel_obs_images (id_image, id_observation) VALUES'
390
			'INSERT INTO cel_obs_images (id_image, id_observation) VALUES'
318
			.' %s '
391
			.' %s '
319
			.'ON DUPLICATE KEY UPDATE id_image = id_image';
392
			.'ON DUPLICATE KEY UPDATE id_image = id_image';
320
		$images_obs_assoc = Array();
393
		$images_obs_assoc = Array();
321
 
394
 
322
		foreach($toutes_images as $images_pour_obs) {
395
		foreach($toutes_images as $images_pour_obs) {
323
			$obs = $enregistrements[$images_pour_obs["obs_pos"]];
396
			$obs = $enregistrements[$images_pour_obs["obs_pos"]];
324
			$id_obs = $lastid // dernier autoinc inséré
397
			$id_obs = $lastid // dernier autoinc inséré
325
				- count($enregistrements) - 1 // correspondrait au premier autoinc
398
				- count($enregistrements) - 1 // correspondrait au premier autoinc
326
				+ $images_pour_obs["obs_pos"]; // ordre d'insertion = ordre dans le tableau $enregistrements
399
				+ $images_pour_obs["obs_pos"]; // ordre d'insertion = ordre dans le tableau $enregistrements
327
			foreach($images_pour_obs['images'] as $image) {
400
			foreach($images_pour_obs['images'] as $image) {
328
				$images_obs_assoc[] = sprintf('(%d,%d)',
401
				$images_obs_assoc[] = sprintf('(%d,%d)',
329
											  $image['id_image'], // intval() useless
402
											  $image['id_image'], // intval() useless
330
											  $id_obs); // intval() useless
403
											  $id_obs); // intval() useless
331
			}
404
			}
332
		}
405
		}
333
 
406
 
334
		if($images_obs_assoc) {
407
		if($images_obs_assoc) {
335
			$requete = sprintf($images_insert, implode(', ', $images_obs_assoc));
408
			$requete = sprintf($images_insert, implode(', ', $images_obs_assoc));
336
			echo "$requete\n";
409
			echo "$requete\n";
337
		}
410
		}
338
	}
411
	}
339
 
412
 
340
 
413
 
341
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
414
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
342
		// en premier car le résultat est utile pour
415
		// en premier car le résultat est utile pour
343
		// traiter longitude et latitude (traiterLonLat())
416
		// traiter longitude et latitude (traiterLonLat())
344
		$referentiel = self::identReferentiel($ligne[C_NOM_REFERENTIEL]);
417
		$referentiel = self::identReferentiel($ligne[C_NOM_REFERENTIEL]);
345
 
418
 
346
		// $espece est rempli de plusieurs informations
419
		// $espece est rempli de plusieurs informations
347
		$espece = Array(C_NOM_SEL => NULL, C_NOM_SEL_NN => NULL, C_NOM_RET => NULL,
420
		$espece = Array(C_NOM_SEL => NULL, C_NOM_SEL_NN => NULL, C_NOM_RET => NULL,
348
						C_NOM_RET_NN => NULL, C_NT => NULL, C_FAMILLE => NULL);
421
						C_NOM_RET_NN => NULL, C_NT => NULL, C_FAMILLE => NULL);
349
		self::traiterEspece($ligne, $espece, $cel);
422
		self::traiterEspece($ligne, $espece, $cel);
350
 
423
 
351
		// $localisation est rempli à partir de plusieurs champs: C_ZONE_GEO et C_CE_ZONE_GEO
424
		// $localisation est rempli à partir de plusieurs champs: C_ZONE_GEO et C_CE_ZONE_GEO
352
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
425
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
353
		self::traiterLocalisation($ligne, $localisation, $cel);
426
		self::traiterLocalisation($ligne, $localisation, $cel);
354
 
427
 
355
		// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
428
		// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
356
		// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
429
		// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
357
		$enregistrement = Array(
430
		$enregistrement = Array(
358
			"ordre" => $dernier_ordre,
431
			"ordre" => $dernier_ordre,
359
 
432
 
360
			// $this->quoteNonNull() est déjà appliquée dans traiterEspece()
433
			// $this->quoteNonNull() est déjà appliquée dans traiterEspece()
361
			"nom_sel" => $espece[C_NOM_SEL],
434
			"nom_sel" => $espece[C_NOM_SEL],
362
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
435
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
363
			"nom_ret" => $espece[C_NOM_RET],
436
			"nom_ret" => $espece[C_NOM_RET],
364
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
437
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
365
			"nt" => $espece[C_NT],
438
			"nt" => $espece[C_NT],
366
			"famille" => $espece[C_FAMILLE],
439
			"famille" => $espece[C_FAMILLE],
367
 
440
 
368
			"nom_referentiel" => $cel->quoteNonNull($referentiel),
441
			"nom_referentiel" => $cel->quoteNonNull($referentiel),
369
 
442
 
370
			// $this->quoteNonNull() est déjà appliquée dans traiterLocalisation()
443
			// $this->quoteNonNull() est déjà appliquée dans traiterLocalisation()
371
			"zone_geo" => $localisation[C_ZONE_GEO],
444
			"zone_geo" => $localisation[C_ZONE_GEO],
372
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
445
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
373
 
446
 
374
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
447
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
375
			"date_observation" => $cel->quoteNonNull(self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ligne)),
448
			"date_observation" => $cel->quoteNonNull(self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ligne)),
376
 
449
 
377
			"lieudit" => $cel->quoteNonNull(trim($ligne[C_LIEUDIT])),
450
			"lieudit" => $cel->quoteNonNull(trim($ligne[C_LIEUDIT])),
378
			"station" => $cel->quoteNonNull(trim($ligne[C_STATION])),
451
			"station" => $cel->quoteNonNull(trim($ligne[C_STATION])),
379
			"milieu" => $cel->quoteNonNull(trim($ligne[C_MILIEU])),
452
			"milieu" => $cel->quoteNonNull(trim($ligne[C_MILIEU])),
380
			"commentaire" => $cel->quoteNonNull(trim($ligne[C_COMMENTAIRE])), // TODO: foreign-key
453
			"commentaire" => NULL, //$cel->quoteNonNull(trim($ligne[C_COMMENTAIRE])), // TODO: foreign-key
381
 
454
 
382
 
455
 
383
			"transmission" => in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0,
456
			"transmission" => in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0,
384
 
457
 
385
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
458
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
386
			"latitude" => self::traiterLonLat(NULL, $ligne[C_LATITUDE], $referentiel, $ligne),
459
			"latitude" => self::traiterLonLat(NULL, $ligne[C_LATITUDE], $referentiel, $ligne),
387
			"longitude" => self::traiterLonLat($ligne[C_LONGITUDE], NULL, $referentiel, $ligne),
460
			"longitude" => self::traiterLonLat($ligne[C_LONGITUDE], NULL, $referentiel, $ligne),
-
 
461
 
-
 
462
			// @ car potentiellement optionnelles ou toutes vides => pas d'index dans PHPExcel (tableau optimisé)
-
 
463
			"abondance" => @$ligne[C_ABONDANCE],
-
 
464
			"certitude" => @$ligne[C_CERTITUDE],
-
 
465
			"phenologie" => @$ligne[C_PHENOLOGIE],
-
 
466
 
-
 
467
			"code_insee_calcule" => $localisation[C_CE_ZONE_GEO] // TODO ?
388
		);
468
		);
389
 
469
 
390
		// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
470
		// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
391
		// que si des résultats sont trouvés
471
		// que si des résultats sont trouvés
392
		// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
472
		// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
393
		if(@$ligne[C_IMAGES]) self::traiterImage($ligne[C_IMAGES], $cel, $enregistrement);
473
		if(@$ligne[C_IMAGES]) self::traiterImage($ligne[C_IMAGES], $cel, $enregistrement);
394
 
474
 
395
		return $enregistrement;
475
		return $enregistrement;
396
	}
476
	}
397
 
477
 
398
	static function traiterImage($str, $cel, &$enregistrement) {
478
	static function traiterImage($str, $cel, &$enregistrement) {
399
		$liste_images = array_filter(explode("/", $str));
479
		$liste_images = array_filter(explode("/", $str));
400
		array_walk($liste_images,
480
		array_walk($liste_images,
401
				   function($item, $key, $obj) { $item = $obj->quoteNonNull(trim($item)); },
481
				   function($item, $key, $obj) { $item = $obj->quoteNonNull_real(trim($item)); },
402
				   $cel);
482
				   $cel);
403
		$requete = sprintf(
483
		$requete = sprintf(
404
			"SELECT id_image, nom_original FROM cel_images WHERE ce_utilisateur = %d AND nom_original IN (\"%s\")",
484
			"SELECT id_image, nom_original FROM cel_images WHERE ce_utilisateur = %d AND nom_original IN (\"%s\")",
405
			$cel->id_utilisateur,
485
			$cel->id_utilisateur,
406
			implode('","', $liste_images));
486
			implode('","', $liste_images));
407
 
487
 
408
		$resultat = $cel->requeter($requete);
488
		$resultat = $cel->requeter($requete);
409
 
489
 
410
		if($resultat) $enregistrement['_images'] = $resultat;
490
		if($resultat) $enregistrement['_images'] = $resultat;
411
	}
491
	}
412
 
492
 
413
 
493
 
414
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
494
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
415
 
495
 
416
	// TODO: PHP 5.3, utiliser date_parse_from_format()
496
	// TODO: PHP 5.3, utiliser date_parse_from_format()
417
	// TODO: parser les heures (cf product-owner)
497
	// TODO: parser les heures (cf product-owner)
418
	// TODO: passer par le timestamp pour s'assurer de la validité
498
	// TODO: passer par le timestamp pour s'assurer de la validité
419
	static function traiterDateObs($date, $ligne) {
499
	static function traiterDateObs($date, $ligne) {
420
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
500
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
421
		if(is_double($date)) {
501
		if(is_double($date)) {
422
			if($date > 0)
502
			if($date > 0)
423
				return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
503
				return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
424
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
504
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
425
						  "Attention: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble",
505
						  "Attention: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble",
426
						  E_USER_NOTICE);
506
						  E_USER_NOTICE);
427
			
507
			
428
			// throw new Exception("erreur: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble");
508
			// throw new Exception("erreur: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble");
429
 
509
 
430
			// attention, UNIX timestamp, car Excel les décompte depuis 1900
510
			// attention, UNIX timestamp, car Excel les décompte depuis 1900
431
			// cf http://fczaja.blogspot.fr/2011/06/convert-excel-date-into-timestamp.html
511
			// cf http://fczaja.blogspot.fr/2011/06/convert-excel-date-into-timestamp.html
432
			// $timestamp = ($date - MIN_DATES_DIFF) * 60 * 60 * 24 - time(); // NON
512
			// $timestamp = ($date - MIN_DATES_DIFF) * 60 * 60 * 24 - time(); // NON
433
 
513
 
434
			// $timestamp = PHPExcel_Calculation::getInstance()->calculateFormula("=" . $date . "-DATE(1970,1,1)*60*60*24"); // NON
514
			// $timestamp = PHPExcel_Calculation::getInstance()->calculateFormula("=" . $date . "-DATE(1970,1,1)*60*60*24"); // NON
435
 
515
 
436
			// echo strftime("%Y/%m/%d 00:00:00", $timestamp); // NON
516
			// echo strftime("%Y/%m/%d 00:00:00", $timestamp); // NON
437
		}
517
		}
438
		else {
518
		else {
439
			$timestamp = strtotime($date);
519
			$timestamp = strtotime($date);
440
			if(! $timestamp) {
520
			if(! $timestamp) {
441
				if($date) trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": Attention: date erronée ($date)", E_USER_NOTICE);
521
				if($date) trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": Attention: date erronée ($date)", E_USER_NOTICE);
442
				return NULL;
522
				return NULL;
443
			}
523
			}
444
			return strftime("%Y-%m-%d 00:00:00", strtotime($date));
524
			return strftime("%Y-%m-%d 00:00:00", strtotime($date));
445
		}
525
		}
446
	}
526
	}
447
 
527
 
448
	static function identReferentiel($referentiel) {
528
	static function identReferentiel($referentiel) {
449
		// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
529
		// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
450
		if(strpos(strtolower($referentiel), 'bdtfx') !== FALSE) return 'bdtfx:v1.01';
530
		if(strpos(strtolower($referentiel), 'bdtfx') !== FALSE) return 'bdtfx:v1.01';
451
		if(strpos(strtolower($referentiel), 'bdtxa') !== FALSE) return 'bdtxa:v1.00';
531
		if(strpos(strtolower($referentiel), 'bdtxa') !== FALSE) return 'bdtxa:v1.00';
452
		if(strpos(strtolower($referentiel), 'bdnff') !== FALSE) return 'bdnff:4.02';
532
		if(strpos(strtolower($referentiel), 'bdnff') !== FALSE) return 'bdnff:4.02';
453
		if(strpos(strtolower($referentiel), 'isfan') !== FALSE) return 'isfan:v1.00';
533
		if(strpos(strtolower($referentiel), 'isfan') !== FALSE) return 'isfan:v1.00';
454
 
534
 
455
		if($referentiel) {
535
		if($referentiel) {
456
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": Attention: référentiel inconnu", E_USER_NOTICE);
536
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": Attention: référentiel inconnu", E_USER_NOTICE);
457
		}
537
		}
458
		return NULL;
538
		return NULL;
459
		/* TODO: cf story,
539
		/* TODO: cf story,
460
		   En cas de NULL faire une seconde passe de détection à partir du nom saisi
540
		   En cas de NULL faire une seconde passe de détection à partir du nom saisi
461
		   + accepter les n° de version */
541
		   + accepter les n° de version */
462
	}
542
	}
463
 
543
 
464
	static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx:v1.01', $ligne) {
544
	static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx:v1.01', $ligne) {
465
		// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
545
		// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
466
		if($lon && is_string($lon)) $lon = str_replace(',', '.', $lon);
546
		if($lon && is_string($lon)) $lon = str_replace(',', '.', $lon);
467
		if($lat && is_string($lat)) $lat = str_replace(',', '.', $lat);
547
		if($lat && is_string($lat)) $lat = str_replace(',', '.', $lat);
468
 
548
 
469
		// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
549
		// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
470
		// tout en uniformisant le format de séparateur des décimales (le ".")
550
		// tout en uniformisant le format de séparateur des décimales (le ".")
471
		if($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) return sprintf('%.5F', $lon);
551
		if($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) return sprintf('%.5F', $lon);
472
		if($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) return sprintf('%.5F', $lat);
552
		if($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) return sprintf('%.5F', $lat);
473
 
553
 
474
		if($lon || $lat) {
554
		if($lon || $lat) {
475
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
555
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
476
						  "Attention: longitude ou latitude erronée",
556
						  "Attention: longitude ou latitude erronée",
477
						  E_USER_NOTICE);
557
						  E_USER_NOTICE);
478
		}
558
		}
479
		return NULL;
559
		return NULL;
480
 
560
 
481
		/* limite france métropole si bdtfx ? ou bdtxa ? ...
561
		/* limite france métropole si bdtfx ? ou bdtxa ? ...
482
		   NON!
562
		   NON!
483
		   Un taxon d'un référentiel donné peut être théoriquement observé n'importe où sur le globe.
563
		   Un taxon d'un référentiel donné peut être théoriquement observé n'importe où sur le globe.
484
		   Il n'y a pas lieu d'effectuer des restriction ici.
564
		   Il n'y a pas lieu d'effectuer des restriction ici.
485
		   Cependant des erreurs fréquentes (0,0 ou lon/lat inversées) peuvent être détectés ici.
565
		   Cependant des erreurs fréquentes (0,0 ou lon/lat inversées) peuvent être détectés ici.
486
		   TODO */
566
		   TODO */
487
		$bbox = self::getReferentielBBox($referentiel);
567
		$bbox = self::getReferentielBBox($referentiel);
488
		if(!$bbox) return NULL;
568
		if(!$bbox) return NULL;
489
 
569
 
490
		if($lon) {
570
		if($lon) {
491
			if($lon < $bbox['EST'] && $lon > $bbox['OUEST']) return is_numeric($lon) ? $lon : NULL;
571
			if($lon < $bbox['EST'] && $lon > $bbox['OUEST']) return is_numeric($lon) ? $lon : NULL;
492
			else return NULL;
572
			else return NULL;
493
		}
573
		}
494
		if($lat) {
574
		if($lat) {
495
			if($lat < $bbox['NORD'] && $lat > $bbox['SUD']) return is_numeric($lat) ? $lat : NULL;
575
			if($lat < $bbox['NORD'] && $lat > $bbox['SUD']) return is_numeric($lat) ? $lat : NULL;
496
			return NULL;
576
			return NULL;
497
		}
577
		}
498
	}
578
	}
499
 
579
 
500
 
580
 
501
	static function traiterEspece($ligne, Array &$espece, $cel) {
581
	static function traiterEspece($ligne, Array &$espece, $cel) {
502
		if(!$ligne[C_NOM_SEL]) return;
582
		if(!$ligne[C_NOM_SEL]) return;
503
 
583
 
504
		$taxon_info_webservice = new RechercheInfosTaxonBeta($cel->config);
584
		$taxon_info_webservice = new RechercheInfosTaxonBeta($cel->config);
505
 
585
 
506
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
586
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
507
		// FALSE = recherche étendue (LIKE x%)
587
		// FALSE = recherche étendue (LIKE x%)
508
		$resultat_recherche_espece = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax($ligne[C_NOM_SEL]);
588
		$resultat_recherche_espece = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax($ligne[C_NOM_SEL]);
509
 
589
 
510
		// on supprime les noms retenus et renvoi tel quel
590
		// on supprime les noms retenus et renvoi tel quel
511
		// on réutilise les define pour les noms d'indexes, tant qu'à faire
591
		// on réutilise les define pour les noms d'indexes, tant qu'à faire
512
		if (empty($resultat_recherche_espece['en_id_nom'])) {
592
		if (empty($resultat_recherche_espece['en_id_nom'])) {
513
			$espece[C_NOM_SEL] = $cel->quoteNonNull(trim($ligne[C_NOM_SEL]));
593
			$espece[C_NOM_SEL] = $cel->quoteNonNull(trim($ligne[C_NOM_SEL]));
514
 
594
 
515
			// le reste reste à NULL
595
			// le reste reste à NULL
516
			// TODO: si empty(C_NOM_SEL) et !empty(C_NOM_SEL_NN) : recherche info à partir de C_NOM_SEL_NN
596
			// TODO: si empty(C_NOM_SEL) et !empty(C_NOM_SEL_NN) : recherche info à partir de C_NOM_SEL_NN
517
			$espece[C_NOM_SEL_NN] = $ligne[C_NOM_SEL_NN];
597
			$espece[C_NOM_SEL_NN] = $ligne[C_NOM_SEL_NN];
518
			$espece[C_NOM_RET] = $ligne[C_NOM_RET];
598
			$espece[C_NOM_RET] = $ligne[C_NOM_RET];
519
			$espece[C_NOM_RET_NN] = $ligne[C_NOM_RET_NN];
599
			$espece[C_NOM_RET_NN] = $ligne[C_NOM_RET_NN];
520
			$espece[C_NT] = $ligne[C_NT];
600
			$espece[C_NT] = $ligne[C_NT];
521
			$espece[C_FAMILLE] = $ligne[C_FAMILLE];
601
			$espece[C_FAMILLE] = $ligne[C_FAMILLE];
522
 
602
 
523
			return;
603
			return;
524
		}
604
		}
525
 
605
 
526
		// succès de la détection, récupération des infos
606
		// succès de la détection, récupération des infos
527
		$espece[C_NOM_SEL] = $cel->quoteNonNull($resultat_recherche_espece['nom_sel']);
607
		$espece[C_NOM_SEL] = $cel->quoteNonNull($resultat_recherche_espece['nom_sel']);
528
		$espece[C_NOM_SEL_NN] = $cel->quoteNonNull($resultat_recherche_espece['en_id_nom']);
608
		$espece[C_NOM_SEL_NN] = $cel->quoteNonNull($resultat_recherche_espece['en_id_nom']);
529
 
609
 
530
		$complement = $taxon_info_webservice->rechercherInformationsComplementairesSurNumNom($resultat_recherche_espece['en_id_nom']);
610
		$complement = $taxon_info_webservice->rechercherInformationsComplementairesSurNumNom($resultat_recherche_espece['en_id_nom']);
531
		$espece[C_NOM_RET] = $cel->quoteNonNull($complement['Nom_Retenu']);
611
		$espece[C_NOM_RET] = $cel->quoteNonNull($complement['Nom_Retenu']);
532
		$espece[C_NOM_RET_NN] = $cel->quoteNonNull($complement['Num_Nom_Retenu']);
612
		$espece[C_NOM_RET_NN] = $cel->quoteNonNull($complement['Num_Nom_Retenu']);
533
		$espece[C_NT] = $cel->quoteNonNull($complement['Num_Taxon']);
613
		$espece[C_NT] = $cel->quoteNonNull($complement['Num_Taxon']);
534
		$espece[C_FAMILLE] = $cel->quoteNonNull($complement['Famille']);
614
		$espece[C_FAMILLE] = $cel->quoteNonNull($complement['Famille']);
535
	}
615
	}
536
 
616
 
537
 
617
 
538
	static function traiterLocalisation($ligne, Array &$localisation, $cel) {
618
	static function traiterLocalisation($ligne, Array &$localisation, $cel) {
539
	    $identifiant_commune = trim($ligne[C_ZONE_GEO]);
619
	    $identifiant_commune = trim($ligne[C_ZONE_GEO]);
540
		if(!$identifiant_commune) {
620
		if(!$identifiant_commune) {
541
			$departement = trim($ligne[C_CE_ZONE_GEO]);
621
			$departement = trim($ligne[C_CE_ZONE_GEO]);
542
			goto testdepartement;
622
			goto testdepartement;
543
		}
623
		}
544
 
624
 
545
 
625
 
546
		$select = "SELECT DISTINCT nom, code  FROM cel_zones_geo";
626
		$select = "SELECT DISTINCT nom, code  FROM cel_zones_geo";
547
	
627
	
548
		if (preg_match('/(.*) \((\d+)\)/', $identifiant_commune, $elements)) {
628
		if (preg_match('/(.*) \((\d+)\)/', $identifiant_commune, $elements)) {
549
			// commune + departement : montpellier (34)
629
			// commune + departement : montpellier (34)
550
			$nom_commune=$elements[1];
630
			$nom_commune=$elements[1];
551
			$code_commune=$elements[2];
631
			$code_commune=$elements[2];
552
	 	    $requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
632
	 	    $requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
553
							   $select, $cel->quoteNonNull($nom_commune), $cel->quoteNonNull($code_commune.'%'));
633
							   $select, $cel->quoteNonNull_real($nom_commune), $cel->quoteNonNull_real($code_commune.'%'));
554
		}
634
		}
555
		elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
635
		elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
556
			// Code insee seul  
636
			// Code insee seul  
557
			$code_insee_commune=$elements[1];
637
			$code_insee_commune=$elements[1];
558
	 	    $requete = sprintf("%s WHERE code = %s", $select, $cel->quoteNonNull($code_insee_commune));
638
	 	    $requete = sprintf("%s WHERE code = %s", $select, $cel->quoteNonNull_real($code_insee_commune));
559
		}
639
		}
560
		else {
640
		else {
561
			// Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
641
			// Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
562
			// on prend le risque ici de retourner une mauvaise Commune
642
			// on prend le risque ici de retourner une mauvaise Commune
563
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
643
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
564
	 	    $requete = sprintf("%s WHERE nom LIKE %s", $select, $cel->quoteNonNull($nom_commune.'%'));
644
	 	    $requete = sprintf("%s WHERE nom LIKE %s", $select, $cel->quoteNonNull_real($nom_commune.'%'));
565
		}
645
		}
566
	
646
	
567
		$resultat_commune = $cel->requeter($requete);
647
		$resultat_commune = $cel->requeter($requete);
568
		// TODO: levenstein sort ?
648
		// TODO: levenstein sort ?
569
 
649
 
570
		// cas de la commune introuvable dans le référentiel
650
		// cas de la commune introuvable dans le référentiel
571
		// réinitialisation aux valeurs du fichier XLS
651
		// réinitialisation aux valeurs du fichier XLS
572
		if(! $resultat_commune) {
652
		if(! $resultat_commune) {
573
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
653
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
574
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
654
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
575
		} else {
655
		} else {
576
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
656
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
577
			$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
657
			$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
578
		}
658
		}
579
 
659
 
580
		$departement = &$localisation[C_CE_ZONE_GEO];
660
		$departement = &$localisation[C_CE_ZONE_GEO];
581
 
661
 
582
	testdepartement:
662
	testdepartement:
583
		if(strpos($departement, "INSEE-C:", 0) === 0) goto protectloc;
663
		if(strpos($departement, "INSEE-C:", 0) === 0) goto protectloc;
584
 
664
 
585
		if(!is_numeric($departement)) goto protectloc; // TODO ?
665
		if(!is_numeric($departement)) goto protectloc; // TODO ?
586
		if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
666
		if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
587
		if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
667
		if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
588
		// if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
668
		// if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
589
 
669
 
590
		$departement = trim($departement); // TODO
670
		$departement = trim($departement); // TODO
591
 
671
 
592
	protectloc:
672
	protectloc:
593
		$localisation[C_ZONE_GEO] = $cel->quoteNonNull($localisation[C_ZONE_GEO]);
673
		$localisation[C_ZONE_GEO] = $cel->quoteNonNull($localisation[C_ZONE_GEO]);
594
		$localisation[C_CE_ZONE_GEO] = $cel->quoteNonNull($localisation[C_CE_ZONE_GEO]);
674
		$localisation[C_CE_ZONE_GEO] = $cel->quoteNonNull($localisation[C_CE_ZONE_GEO]);
595
	}
675
	}
596
 
676
 
597
 
677
 
598
 
678
 
599
	/* HELPERS */
679
	/* HELPERS */
600
 
680
 
601
	// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
681
	// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
602
	static function sortArrayByArray($array, $orderArray) {
682
	static function sortArrayByArray($array, $orderArray) {
603
		$ordered = array();
683
		$ordered = array();
604
		foreach($orderArray as $key) {
684
		foreach($orderArray as $key) {
605
			if(array_key_exists($key, $array)) {
685
			if(array_key_exists($key, $array)) {
606
				$ordered[$key] = $array[$key];
686
				$ordered[$key] = $array[$key];
607
				unset($array[$key]);
687
				unset($array[$key]);
608
			}
688
			}
609
		}
689
		}
610
		return $ordered + $array;
690
		return $ordered + $array;
611
	}
691
	}
612
 
692
 
613
	// retourne une BBox [N,S,E,O) pour un référentiel donné
693
	// retourne une BBox [N,S,E,O) pour un référentiel donné
614
	static function getReferentielBBox($referentiel) {
694
	static function getReferentielBBox($referentiel) {
615
		if($referentiel == 'bdtfx:v1.01') return Array(
695
		if($referentiel == 'bdtfx:v1.01') return Array(
616
			'NORD' => 51.2, // Dunkerque
696
			'NORD' => 51.2, // Dunkerque
617
			'SUD' => 41.3, // Bonifacio
697
			'SUD' => 41.3, // Bonifacio
618
			'EST' => 9.7, // Corse
698
			'EST' => 9.7, // Corse
619
			'OUEST' => -5.2); // Ouessan
699
			'OUEST' => -5.2); // Ouessan
620
		return FALSE;
700
		return FALSE;
621
	}
701
	}
622
 
702
 
623
 
703
 
624
	public function initialiser_colonnes_statiques() {
704
	public function initialiser_colonnes_statiques() {
625
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
705
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
626
												Array(
706
												Array(
627
													"ce_utilisateur" => $this->id_utilisateur,
707
													"ce_utilisateur" => $this->id_utilisateur,
628
													"prenom_utilisateur" => $this->quoteNonNull($this->utilisateur['prenom']),
708
													"prenom_utilisateur" => $this->quoteNonNull_real($this->utilisateur['prenom']),
629
													"nom_utilisateur" => $this->quoteNonNull($this->utilisateur['nom']),
709
													"nom_utilisateur" => $this->quoteNonNull_real($this->utilisateur['nom']),
630
													"courriel_utilisateur" => $this->quoteNonNull($this->utilisateur['courriel']),
710
													"courriel_utilisateur" => $this->quoteNonNull_real($this->utilisateur['courriel']),
631
												));
711
												));
632
 
712
 
633
	}
713
	}
-
 
714
 
-
 
715
	private function quoteNonNull_real($chaine) {
-
 
716
		if(is_null($chaine)) return "NULL";
-
 
717
		if(!is_string($chaine)) die("erreur __FILE__, __LINE__");
-
 
718
		return $this->bdd->quote($chaine);
-
 
719
	}
634
 
720
 
635
	// équivalent à CEL->Bdd->proteger() (qui wrap PDO::quote),
721
	// équivalent à CEL->Bdd->proteger() (qui wrap PDO::quote),
636
	// sans transformer NULL en ""
722
	// sans transformer NULL en ""
637
	private function quoteNonNull($chaine) {
723
	private function quoteNonNull($chaine) {
-
 
724
		return $chaine;
638
		if(is_null($chaine)) return "NULL";
725
		if(is_null($chaine)) return "NULL";
639
		if(!is_string($chaine)) die("erreur __FILE__, __LINE__");
726
		if(!is_string($chaine)) die("erreur __FILE__, __LINE__");
640
		return $this->bdd->quote($chaine);
727
		return $this->bdd->quote($chaine);
641
	}
728
	}
642
 
729
 
643
	public function erreurs_stock($errno, $errstr) {
730
	public function erreurs_stock($errno, $errstr) {
644
		$this->bilan[] = $errstr;
731
		$this->bilan[] = $errstr;
645
	}
732
	}
646
}
733
}