Subversion Repositories eFlore/Applications.cel

Rev

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

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