Subversion Repositories eFlore/Applications.cel

Rev

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

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