Subversion Repositories eFlore/Applications.cel

Rev

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

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