Subversion Repositories eFlore/Applications.cel

Rev

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

Rev 2676 Rev 2751
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
		// le cast en string des nombres permet d'unifier le parsing du retour
328
		// le cast en string des nombres permet d'unifier le parsing du retour
329
		// car il n'est destiné qu'à être affiché 
329
		// car il n'est destiné qu'à être affiché 
330
		$retour = array(
330
		$retour = array(
331
					'import_obs_ajoutees' => (string)$obs_ajouts,
331
					'import_obs_ajoutees' => (string)$obs_ajouts,
332
					'import_images_ajoutees' => (string)$nb_images_ajoutees,
332
					'import_images_ajoutees' => (string)$nb_images_ajoutees,
333
					'import_mots_cles_ajoutes' => (string)$nb_mots_cle_ajoutes,
333
					'import_mots_cles_ajoutes' => (string)$nb_mots_cle_ajoutes,
334
					'import_colonnes_non_traitees' => implode(', ', $filtre->exclues)
334
					'import_colonnes_non_traitees' => implode(', ', $filtre->exclues)
335
		);
335
		);
336
		// Ajout d'éventuelles erreurs
336
		// Ajout d'éventuelles erreurs
337
		if ($this->bilan) {
337
		if ($this->bilan) {
338
			$retour += array('import_erreurs' => implode("\n", $this->bilan) . "\n");
338
			$retour += array('import_erreurs' => implode("\n", $this->bilan) . "\n");
339
		}
339
		}
340
		// Dans le cas où le client ne sait pas lire le retour d'upload
340
		// Dans le cas où le client ne sait pas lire le retour d'upload
341
		// on stocke les stats en session pour les appeler plus tard
341
		// on stocke les stats en session pour les appeler plus tard
342
		// car ceci peut poser notamment problème pour les requêtes CORS
342
		// car ceci peut poser notamment problème pour les requêtes CORS
343
		$_SESSION['upload_stats'] = $retour;
343
		$_SESSION['upload_stats'] = $retour;
344
		// On envoie quand même les stats pour les clients qui savent ou peuvent 
344
		// On envoie quand même les stats pour les clients qui savent ou peuvent 
345
		// les lire directement après l'upload
345
		// les lire directement après l'upload
346
		$this->envoyerJson($retour);
346
		$this->envoyerJson($retour);
347
		die();
347
		die();
348
	}
348
	}
-
 
349
	
-
 
350
	public function getElement($uid) {
-
 
351
		if($uid[0] == "template") {
-
 
352
			//$tpl = '/home/aurelien/web/cel_GWT2/jrest/modele_import.xls';
-
 
353
			$tpl = dirname(__FILE__).DIRECTORY_SEPARATOR.'squelettes'.DIRECTORY_SEPARATOR.'modele_import.xls';
-
 
354
			//echo $tpl;exit;
-
 
355
			$lecteur = PHPExcel_IOFactory::createReaderForFile($tpl);
-
 
356
			$classeur_tpl = $lecteur->load($tpl);
-
 
357
			$feuille_tpl = $classeur_tpl->getActiveSheet();
-
 
358
			
-
 
359
			$lettre_colonne_max = $feuille_tpl->getHighestColumn();
-
 
360
			$nb_colonne_max = PHPExcel_Cell::columnIndexFromString($lettre_colonne_max);		
-
 
361
			$ligne = 1;
-
 
362
			$nb_colonne_en_cours = $nb_colonne_max;			
-
 
363
			
-
 
364
			$nom_fichier = 'import';
-
 
365
			if(!empty($_GET['groupe'])) {
-
 
366
				$requete = "SELECT * FROM cel_catalogue_champs_etendus_liaison WHERE GROUPE = ".Cel::db()->proteger($_GET['groupe']);
-
 
367
				$champs = Cel::db()->requeter($requete);
-
 
368
				
-
 
369
				foreach($champs as $champ) {	
-
 
370
					$lettre_colonne = PHPExcel_Cell::stringFromColumnIndex($nb_colonne_en_cours);
-
 
371
					$feuille_tpl->setCellValue($lettre_colonne.$ligne, 'ext:'.$champ['champ']);
-
 
372
					$nb_colonne_en_cours++;
-
 
373
				}		
-
 
374
				$nom_fichier .= '_'.$_GET['groupe'];
-
 
375
			}
-
 
376
 
-
 
377
			header('Content-type: application/vnd.ms-excel');	
-
 
378
			header('Content-Disposition: attachment; filename="'.$nom_fichier.'.xls"');
-
 
379
			$generateur = PHPExcel_IOFactory::createWriter($classeur_tpl, 'Excel5');
-
 
380
			$generateur->save('php://output');
-
 
381
			
-
 
382
			exit;
-
 
383
		}
-
 
384
	}
349
	
385
	
350
	public function getRessource() {
386
	public function getRessource() {
351
		return self::getStatsDernierUpload();
387
		return self::getStatsDernierUpload();
352
	}
388
	}
353
	
389
	
354
	static function getStatsDernierUpload() {
390
	static function getStatsDernierUpload() {
355
		// renvoi des statistiques du dernier envoi de fichier
391
		// renvoi des statistiques du dernier envoi de fichier
356
		$stats = !empty($_SESSION['upload_stats']) ? $_SESSION['upload_stats'] : null;
392
		$stats = !empty($_SESSION['upload_stats']) ? $_SESSION['upload_stats'] : null;
357
		header("Content-Type: application/json; charset=utf-8");
393
		header("Content-Type: application/json; charset=utf-8");
358
		echo json_encode($stats);
394
		echo json_encode($stats);
359
		die();
395
		die();
360
	}
396
	}
361
 
397
 
362
	/* detectionEntete() sert deux rôles:
398
	/* detectionEntete() sert deux rôles:
363
	 1) détecter le type de colonne attendu à partir des textes de la ligne d'en-tête afin de define()
399
	 1) détecter le type de colonne attendu à partir des textes de la ligne d'en-tête afin de define()
364
	 2) permet d'identifier les colonnes non-supportées/inutiles afin d'alléger le processus de parsing de PHPExcel
400
	 2) permet d'identifier les colonnes non-supportées/inutiles afin d'alléger le processus de parsing de PHPExcel
365
	 grace au ReadFilter (C'est le rôle de la valeur de retour)
401
	 grace au ReadFilter (C'est le rôle de la valeur de retour)
366
 
402
 
367
	 La raison de la présence du paramètre $numeric_keys est que pour réussir à identifier les colonnes à exclure nous
403
	 La raison de la présence du paramètre $numeric_keys est que pour réussir à identifier les colonnes à exclure nous
368
	 devons traiter un tableau représentant la ligne d'en-tête aussi bien:
404
	 devons traiter un tableau représentant la ligne d'en-tête aussi bien:
369
	  - sous forme associative pour PHPExcel (les clefs sont les lettres de l'alphabet)
405
	  - sous forme associative pour PHPExcel (les clefs sont les lettres de l'alphabet)
370
	  - sous forme de clefs numériques (fgetcsv())
406
	  - sous forme de clefs numériques (fgetcsv())
371
	  Le détecter après coup est difficile et pourtant cette distinction est importante car le comportement
407
	  Le détecter après coup est difficile et pourtant cette distinction est importante car le comportement
372
	  d'array_merge() (réordonnancement des clefs numérique) n'est pas souhaitable dans le second cas. */
408
	  d'array_merge() (réordonnancement des clefs numérique) n'est pas souhaitable dans le second cas. */
373
	static function detectionEntete($entete, $numeric_keys = FALSE) {
409
	static function detectionEntete($entete, $numeric_keys = FALSE) {
374
		$colonnes_reconnues = Array();
410
		$colonnes_reconnues = Array();
375
		$cols = FormateurGroupeColonne::nomEnsembleVersListeColonnes('standard,avance');
411
		$cols = FormateurGroupeColonne::nomEnsembleVersListeColonnes('standard,avance');
376
 
412
 
377
		foreach ($entete as $k => $v) {
413
		foreach ($entete as $k => $v) {
378
			// traite les colonnes en faisant fi de la casse et des accents
414
			// traite les colonnes en faisant fi de la casse et des accents
379
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
415
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
380
			foreach ($cols as $col) {
416
			foreach ($cols as $col) {
381
				$entete_officiel_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($col['nom'])));
417
				$entete_officiel_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($col['nom'])));
382
				$entete_officiel_abbrev = $col['abbrev'];
418
				$entete_officiel_abbrev = $col['abbrev'];
383
				if ($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
419
				if ($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
384
					// debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
420
					// debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
385
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
421
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
386
					$colonnes_reconnues[$k] = 1;
422
					$colonnes_reconnues[$k] = 1;
387
					break;
423
					break;
388
				}
424
				}
389
 
425
 
390
				if (strpos($v, self::$prefixe_colonnes_etendues) === 0) {
426
				if (strpos($v, self::$prefixe_colonnes_etendues) === 0) {
391
					$colonnes_reconnues[$k] = 1;
427
					$colonnes_reconnues[$k] = 1;
392
					self::$indexes_colonnes_etendues[$k] = $v;
428
					self::$indexes_colonnes_etendues[$k] = $v;
393
					break;
429
					break;
394
				}
430
				}
395
			}
431
			}
396
		}
432
		}
397
 
433
 
398
		// défini tous les index que nous utilisons à une valeur d'index de colonne Excel qui n'existe pas dans
434
		// défini tous les index que nous utilisons à une valeur d'index de colonne Excel qui n'existe pas dans
399
		// le tableau renvoyé par PHPExcel
435
		// le tableau renvoyé par PHPExcel
400
		// Attention cependant d'utiliser des indexes différenciés car traiterLonLat() et traiterEspece()
436
		// Attention cependant d'utiliser des indexes différenciés car traiterLonLat() et traiterEspece()
401
		// les utilisent
437
		// les utilisent
402
		foreach ($cols as $col) {
438
		foreach ($cols as $col) {
403
			if (!defined('C_'.strtoupper($col['abbrev']))) {
439
			if (!defined('C_'.strtoupper($col['abbrev']))) {
404
				define('C_'.strtoupper($col['abbrev']), 'C_'.strtoupper($col['abbrev']));
440
				define('C_'.strtoupper($col['abbrev']), 'C_'.strtoupper($col['abbrev']));
405
			}
441
			}
406
		}
442
		}
407
 
443
 
408
		// prépare le filtre de PHPExcel qui évitera le traitement de toutes les colonnes superflues
444
		// prépare le filtre de PHPExcel qui évitera le traitement de toutes les colonnes superflues
409
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
445
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
410
 
446
 
411
		// des colonnes de FormateurGroupeColonne::nomEnsembleVersListeColonnes()
447
		// des colonnes de FormateurGroupeColonne::nomEnsembleVersListeColonnes()
412
		// ne retient que celles marquées "importables"
448
		// ne retient que celles marquées "importables"
413
		$colonnes_automatiques = array_filter($cols, '__anonyme_1');
449
		$colonnes_automatiques = array_filter($cols, '__anonyme_1');
414
 
450
 
415
		// ne conserve que le nom long pour matcher avec la ligne XLS d'entête
451
		// ne conserve que le nom long pour matcher avec la ligne XLS d'entête
416
		array_walk($colonnes_automatiques, '__anonyme_2');
452
		array_walk($colonnes_automatiques, '__anonyme_2');
417
 
453
 
418
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
454
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
419
 
455
 
420
		if ($numeric_keys) {
456
		if ($numeric_keys) {
421
			return $colonnesID_non_reconnues + $colonnesID_a_exclure;
457
			return $colonnesID_non_reconnues + $colonnesID_a_exclure;
422
		}
458
		}
423
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
459
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
424
	}
460
	}
425
 
461
 
426
	static function chargerCorrespondancesIdOrdre($cel, $lignes) {
462
	static function chargerCorrespondancesIdOrdre($cel, $lignes) {
427
		$ordresObs = array();
463
		$ordresObs = array();
428
		foreach ($lignes as &$ligne) {
464
		foreach ($lignes as &$ligne) {
429
			$ordresObs[] = $ligne['ordre'];
465
			$ordresObs[] = $ligne['ordre'];
430
		}
466
		}
431
		$ordresObsConcat = implode(',', $ordresObs);
467
		$ordresObsConcat = implode(',', $ordresObs);
432
		$idUtilisateurP = Cel::db()->proteger($cel->id_utilisateur);
468
		$idUtilisateurP = Cel::db()->proteger($cel->id_utilisateur);
433
		$requete = 'SELECT id_observation, ordre '.
469
		$requete = 'SELECT id_observation, ordre '.
434
			'FROM cel_obs '.
470
			'FROM cel_obs '.
435
			"WHERE ordre IN ($ordresObsConcat) ".
471
			"WHERE ordre IN ($ordresObsConcat) ".
436
			"AND ce_utilisateur = $idUtilisateurP ".
472
			"AND ce_utilisateur = $idUtilisateurP ".
437
			' -- '.__FILE__.':'.__LINE__;
473
			' -- '.__FILE__.':'.__LINE__;
438
		$resultats = Cel::db()->requeter($requete);
474
		$resultats = Cel::db()->requeter($requete);
439
		$ordresIds = array();
475
		$ordresIds = array();
440
		foreach ($resultats as &$infos) {
476
		foreach ($resultats as &$infos) {
441
			$ordresIds[$infos['ordre']] = $infos['id_observation'];
477
			$ordresIds[$infos['ordre']] = $infos['id_observation'];
442
		}
478
		}
443
		return $ordresIds;
479
		return $ordresIds;
444
	}
480
	}
445
 
481
 
446
	/*
482
	/*
447
	 * charge un groupe de lignes
483
	 * charge un groupe de lignes
448
	 */
484
	 */
449
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
485
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
450
		$enregistrement = NULL;
486
		$enregistrement = NULL;
451
		$enregistrements = array();
487
		$enregistrements = array();
452
		$toutes_images = array();
488
		$toutes_images = array();
453
		$tous_mots_cle = array();
489
		$tous_mots_cle = array();
454
		$tous_champs_etendus = array();
490
		$tous_champs_etendus = array();
455
 
491
 
456
		foreach ($lignes as $ligne) {
492
		foreach ($lignes as $ligne) {
457
			// dans le cas de fgetcsv, on peut avoir des false additionnel (cf do/while l. 279)
493
			// dans le cas de fgetcsv, on peut avoir des false additionnel (cf do/while l. 279)
458
			if ($ligne === false) {
494
			if ($ligne === false) {
459
				continue;
495
				continue;
460
			}
496
			}
461
 
497
 
462
			// on a besoin des NULL pour éviter des notice d'index indéfini
498
			// on a besoin des NULL pour éviter des notice d'index indéfini
463
			if (! array_filter($ligne, '__anonyme_3')) {
499
			if (! array_filter($ligne, '__anonyme_3')) {
464
				continue;
500
				continue;
465
			}
501
			}
466
 
502
 
467
			if ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) {
503
			if ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) {
468
				// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
504
				// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
469
				$enregistrements[] = $enregistrement;
505
				$enregistrements[] = $enregistrement;
470
				$pos = count($enregistrements) - 1;
506
				$pos = count($enregistrements) - 1;
471
				$last = &$enregistrements[$pos];
507
				$last = &$enregistrements[$pos];
472
 
508
 
473
				if (isset($enregistrement['_images'])) {
509
				if (isset($enregistrement['_images'])) {
474
					// ne dépend pas de cel_obs, et seront insérées *après* les enregistrements
510
					// ne dépend pas de cel_obs, et seront insérées *après* les enregistrements
475
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
511
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
476
					$toutes_images[] = array(
512
					$toutes_images[] = array(
477
						'images' => $last['_images'],
513
						'images' => $last['_images'],
478
						'obs_pos' => $pos);
514
						'obs_pos' => $pos);
479
					// ce champ n'a pas à faire partie de l'insertion dans cel_obs,
515
					// ce champ n'a pas à faire partie de l'insertion dans cel_obs,
480
					// mais est utile pour la liaison avec les images
516
					// mais est utile pour la liaison avec les images
481
					unset($last['_images']);
517
					unset($last['_images']);
482
				}
518
				}
483
 
519
 
484
				if (isset($enregistrement['_mots_cle'])) {
520
				if (isset($enregistrement['_mots_cle'])) {
485
					// ne dépend pas de cel_obs, et seront insérés *après* les enregistrements
521
					// ne dépend pas de cel_obs, et seront insérés *après* les enregistrements
486
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
522
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
487
					$tous_mots_cle[] = array(
523
					$tous_mots_cle[] = array(
488
						'mots_cle' => $last['_mots_cle'],
524
						'mots_cle' => $last['_mots_cle'],
489
						'obs_pos' => $pos);
525
						'obs_pos' => $pos);
490
					unset($last['_mots_cle']);
526
					unset($last['_mots_cle']);
491
				}
527
				}
492
 
528
 
493
				if (isset($enregistrement['_champs_etendus'])) {
529
				if (isset($enregistrement['_champs_etendus'])) {
494
					$tous_champs_etendus[] = array(
530
					$tous_champs_etendus[] = array(
495
						'champs_etendus' => $last['_champs_etendus'],
531
						'champs_etendus' => $last['_champs_etendus'],
496
						'ordre' => $dernier_ordre);
532
						'ordre' => $dernier_ordre);
497
					unset($last['_champs_etendus']);
533
					unset($last['_champs_etendus']);
498
				}
534
				}
499
				$dernier_ordre++;
535
				$dernier_ordre++;
500
			}
536
			}
501
		}
537
		}
502
		return array($enregistrements, $toutes_images, $tous_mots_cle, $tous_champs_etendus);
538
		return array($enregistrements, $toutes_images, $tous_mots_cle, $tous_champs_etendus);
503
	}
539
	}
504
 
540
 
505
	static function trierColonnes(&$enregistrements) {
541
	static function trierColonnes(&$enregistrements) {
506
		foreach ($enregistrements as &$enregistrement) {
542
		foreach ($enregistrements as &$enregistrement) {
507
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
543
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
508
		}
544
		}
509
	}
545
	}
510
 
546
 
511
	static function stockerMotsCle($enregistrements, $tous_mots_cle, $lastid) {
547
	static function stockerMotsCle($enregistrements, $tous_mots_cle, $lastid) {
512
		$c = 0;
548
		$c = 0;
513
		// debug: var_dump($tous_mots_cle);die;
549
		// debug: var_dump($tous_mots_cle);die;
514
		foreach ($tous_mots_cle as $v) {
550
		foreach ($tous_mots_cle as $v) {
515
			$c += count($v['mots_cle']['to_insert']);
551
			$c += count($v['mots_cle']['to_insert']);
516
		}
552
		}
517
		return $c;
553
		return $c;
518
	}
554
	}
519
 
555
 
520
	static function stockerImages($enregistrements, $toutes_images, $ordre_ids) {
556
	static function stockerImages($enregistrements, $toutes_images, $ordre_ids) {
521
		$valuesSql = array();
557
		$valuesSql = array();
522
		foreach ($toutes_images as $images_pour_obs) {
558
		foreach ($toutes_images as $images_pour_obs) {
523
			$obs = $enregistrements[$images_pour_obs['obs_pos']];
559
			$obs = $enregistrements[$images_pour_obs['obs_pos']];
524
			$id_obs = $ordre_ids[$obs['ordre']]; // id réel de l'observation correspondant à l'ordre
560
			$id_obs = $ordre_ids[$obs['ordre']]; // id réel de l'observation correspondant à l'ordre
525
			$transmission = $obs['transmission'];
561
			$transmission = $obs['transmission'];
526
			$date_transmission = $obs['date_transmission'];
562
			$date_transmission = $obs['date_transmission'];
527
			foreach ($images_pour_obs['images'] as $image) {
563
			foreach ($images_pour_obs['images'] as $image) {
528
				$id_img = $image['id_image'];
564
				$id_img = $image['id_image'];
529
				$valuesSql[] = "($id_img, $id_obs, NOW(), $transmission, $date_transmission)";
565
				$valuesSql[] = "($id_img, $id_obs, NOW(), $transmission, $date_transmission)";
530
			}
566
			}
531
		}
567
		}
532
 
568
 
533
		if ($valuesSql) {
569
		if ($valuesSql) {
534
			$clauseValues = implode(', ', $valuesSql);
570
			$clauseValues = implode(', ', $valuesSql);
535
			// Utilisation de INSERT pour faire des UPDATE multiples en une seule requête
571
			// Utilisation de INSERT pour faire des UPDATE multiples en une seule requête
536
			$requete = 'INSERT INTO cel_images '.
572
			$requete = 'INSERT INTO cel_images '.
537
				'(id_image, ce_observation, date_liaison, transmission, date_transmission) '.
573
				'(id_image, ce_observation, date_liaison, transmission, date_transmission) '.
538
				"VALUES $clauseValues ".
574
				"VALUES $clauseValues ".
539
				'ON DUPLICATE KEY UPDATE '.
575
				'ON DUPLICATE KEY UPDATE '.
540
				'ce_observation = VALUES(ce_observation), '.
576
				'ce_observation = VALUES(ce_observation), '.
541
				'date_liaison = NOW(), '.
577
				'date_liaison = NOW(), '.
542
				'transmission = VALUES(transmission), '.
578
				'transmission = VALUES(transmission), '.
543
				'date_transmission = VALUES(date_transmission) '.
579
				'date_transmission = VALUES(date_transmission) '.
544
				' -- '.__FILE__.':'.__LINE__;
580
				' -- '.__FILE__.':'.__LINE__;
545
			Cel::db()->executer($requete);
581
			Cel::db()->executer($requete);
546
		}
582
		}
547
		return count($valuesSql);
583
		return count($valuesSql);
548
	}
584
	}
549
 
585
 
550
	/*
586
	/*
551
	 Aucune des valeurs présentes dans $enregistrement n'est quotée
587
	 Aucune des valeurs présentes dans $enregistrement n'est quotée
552
	 cad aucune des valeurs retournée par traiter{Espece|Localisation}()
588
	 cad aucune des valeurs retournée par traiter{Espece|Localisation}()
553
	 car ce tableau est passé à un PDO::preparedStatement() qui applique
589
	 car ce tableau est passé à un PDO::preparedStatement() qui applique
554
	  proprement les règle d'échappement.
590
	  proprement les règle d'échappement.
555
	*/
591
	*/
556
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
592
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
557
		// évite des notices d'index lors des trigger_error()
593
		// évite des notices d'index lors des trigger_error()
558
		$ref_ligne = !empty($ligne[C_NOM_SEL]) ? trim($ligne[C_NOM_SEL]) : '';
594
		$ref_ligne = !empty($ligne[C_NOM_SEL]) ? trim($ligne[C_NOM_SEL]) : '';
559
 
595
 
560
		// en premier car le résultat est utile pour
596
		// en premier car le résultat est utile pour
561
		// * traiter espèce (traiterEspece())
597
		// * traiter espèce (traiterEspece())
562
		// * traiter longitude et latitude (traiterLonLat())
598
		// * traiter longitude et latitude (traiterLonLat())
563
		$referentiel = self::identReferentiel(trim(strtolower(@$ligne[C_NOM_REFERENTIEL])), $ligne, $ref_ligne);
599
		$referentiel = self::identReferentiel(trim(strtolower(@$ligne[C_NOM_REFERENTIEL])), $ligne, $ref_ligne);
564
 
600
 
565
		// $espece est rempli de plusieurs informations
601
		// $espece est rempli de plusieurs informations
566
		$espece = array(
602
		$espece = array(
567
			C_NOM_SEL => NULL,
603
			C_NOM_SEL => NULL,
568
			C_NOM_SEL_NN => NULL,
604
			C_NOM_SEL_NN => NULL,
569
			C_NOM_RET => NULL,
605
			C_NOM_RET => NULL,
570
			C_NOM_RET_NN => NULL,
606
			C_NOM_RET_NN => NULL,
571
			C_NT => NULL,
607
			C_NT => NULL,
572
			C_FAMILLE => NULL);
608
			C_FAMILLE => NULL);
573
		self::traiterEspece($ligne, $espece, $referentiel, $cel->taxon_info_webservice);
609
		self::traiterEspece($ligne, $espece, $referentiel, $cel->taxon_info_webservice);
574
 
610
 
575
		if (!$espece[C_NOM_SEL]) {
611
		if (!$espece[C_NOM_SEL]) {
576
			$referentiel = Cel::$fallback_referentiel;
612
			$referentiel = Cel::$fallback_referentiel;
577
		}
613
		}
578
		if ($espece[C_NOM_SEL] && !$espece[C_NOM_SEL_NN]) {
614
		if ($espece[C_NOM_SEL] && !$espece[C_NOM_SEL_NN]) {
579
			$referentiel = Cel::$fallback_referentiel;
615
			$referentiel = Cel::$fallback_referentiel;
580
		}
616
		}
581
 
617
 
582
		// $localisation est rempli à partir de plusieurs champs: C_ZONE_GEO et C_CE_ZONE_GEO
618
		// $localisation est rempli à partir de plusieurs champs: C_ZONE_GEO et C_CE_ZONE_GEO
583
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
619
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
584
		self::traiterLocalisation($ligne, $localisation);
620
		self::traiterLocalisation($ligne, $localisation);
585
		//TODO: le jour où c'est efficace, traiter le pays à l'import
621
		//TODO: le jour où c'est efficace, traiter le pays à l'import
586
 
622
 
587
		// $transmission est utilisé pour date_transmission
623
		// $transmission est utilisé pour date_transmission
588
		// XXX: @ contre "Undefined index"
624
		// XXX: @ contre "Undefined index"
589
		@$transmission = in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0;
625
		@$transmission = in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0;
590
 
626
 
591
 
627
 
592
		// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
628
		// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
593
		// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
629
		// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
594
		$enregistrement = array(
630
		$enregistrement = array(
595
			"ordre" => $dernier_ordre,
631
			"ordre" => $dernier_ordre,
596
 
632
 
597
			"nom_sel" => $espece[C_NOM_SEL],
633
			"nom_sel" => $espece[C_NOM_SEL],
598
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
634
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
599
			"nom_ret" => $espece[C_NOM_RET],
635
			"nom_ret" => $espece[C_NOM_RET],
600
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
636
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
601
			"nt" => $espece[C_NT],
637
			"nt" => $espece[C_NT],
602
			"famille" => $espece[C_FAMILLE],
638
			"famille" => $espece[C_FAMILLE],
603
 
639
 
604
			"nom_referentiel" => $referentiel,
640
			"nom_referentiel" => $referentiel,
605
 
641
 
606
			"pays" => $ligne[C_PAYS],
642
			"pays" => $ligne[C_PAYS],
607
			"zone_geo" => $localisation[C_ZONE_GEO],
643
			"zone_geo" => $localisation[C_ZONE_GEO],
608
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
644
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
609
 
645
 
610
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
646
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
611
			"date_observation" => isset($ligne[C_DATE_OBSERVATION]) ? self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ref_ligne) : null,
647
			"date_observation" => isset($ligne[C_DATE_OBSERVATION]) ? self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ref_ligne) : null,
612
 
648
 
613
			"lieudit" => isset($ligne[C_LIEUDIT]) ? trim($ligne[C_LIEUDIT]) : null,
649
			"lieudit" => isset($ligne[C_LIEUDIT]) ? trim($ligne[C_LIEUDIT]) : null,
614
			"station" => isset($ligne[C_STATION]) ? trim($ligne[C_STATION]) : null,
650
			"station" => isset($ligne[C_STATION]) ? trim($ligne[C_STATION]) : null,
615
			"milieu" => isset($ligne[C_MILIEU]) ? trim($ligne[C_MILIEU]) : null,
651
			"milieu" => isset($ligne[C_MILIEU]) ? trim($ligne[C_MILIEU]) : null,
616
 
652
 
617
			"mots_cles_texte" => NULL, // TODO: foreign-key
653
			"mots_cles_texte" => NULL, // TODO: foreign-key
618
			// XXX: @ contre "Undefined index"
654
			// XXX: @ contre "Undefined index"
619
			"commentaire" => isset($ligne[C_COMMENTAIRE]) ? trim($ligne[C_COMMENTAIRE]) : null,
655
			"commentaire" => isset($ligne[C_COMMENTAIRE]) ? trim($ligne[C_COMMENTAIRE]) : null,
620
 
656
 
621
			"transmission" => $transmission,
657
			"transmission" => $transmission,
622
			"date_transmission" => $transmission ? date('Y-m-d H:i:s') : null, // pas de fonction SQL dans un PDO statement, <=> now()
658
			"date_transmission" => $transmission ? date('Y-m-d H:i:s') : null, // pas de fonction SQL dans un PDO statement, <=> now()
623
 
659
 
624
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
660
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
625
			"latitude" => isset($ligne[C_LATITUDE]) ? self::traiterLonLat(null, $ligne[C_LATITUDE], $referentiel, $ref_ligne) : null,
661
			"latitude" => isset($ligne[C_LATITUDE]) ? self::traiterLonLat(null, $ligne[C_LATITUDE], $referentiel, $ref_ligne) : null,
626
			"longitude" => isset($ligne[C_LONGITUDE]) ? self::traiterLonLat($ligne[C_LONGITUDE], null, $referentiel, $ref_ligne) : null,
662
			"longitude" => isset($ligne[C_LONGITUDE]) ? self::traiterLonLat($ligne[C_LONGITUDE], null, $referentiel, $ref_ligne) : null,
627
			"altitude" => isset($ligne[C_ALTITUDE]) ? intval($ligne[C_ALTITUDE]) : null, // TODO: guess alt from lon/lat
663
			"altitude" => isset($ligne[C_ALTITUDE]) ? intval($ligne[C_ALTITUDE]) : null, // TODO: guess alt from lon/lat
628
 
664
 
629
			// @ car potentiellement optionnelles ou toutes vides => pas d'index dans PHPExcel (tableau optimisé)
665
			// @ car potentiellement optionnelles ou toutes vides => pas d'index dans PHPExcel (tableau optimisé)
630
			"abondance" => @$ligne[C_ABONDANCE],
666
			"abondance" => @$ligne[C_ABONDANCE],
631
			"certitude" => @$ligne[C_CERTITUDE],
667
			"certitude" => @$ligne[C_CERTITUDE],
632
			"phenologie" => @$ligne[C_PHENOLOGIE],
668
			"phenologie" => @$ligne[C_PHENOLOGIE],
633
 
669
 
634
			"code_insee_calcule" => substr($localisation[C_CE_ZONE_GEO], -5) // varchar(5)
670
			"code_insee_calcule" => substr($localisation[C_CE_ZONE_GEO], -5) // varchar(5)
635
		);
671
		);
636
 
672
 
637
		// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
673
		// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
638
		// que si des résultats sont trouvés
674
		// que si des résultats sont trouvés
639
		// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
675
		// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
640
		if (@$ligne[C_IMAGES]) {
676
		if (@$ligne[C_IMAGES]) {
641
			self::traiterImage($ligne[C_IMAGES], $cel->id_utilisateur, $enregistrement);
677
			self::traiterImage($ligne[C_IMAGES], $cel->id_utilisateur, $enregistrement);
642
		}
678
		}
643
 
679
 
644
		if (@$ligne[C_MOTS_CLES_TEXTE]) {
680
		if (@$ligne[C_MOTS_CLES_TEXTE]) {
645
			self::traiterMotsCle($ligne[C_MOTS_CLES_TEXTE], $cel->id_utilisateur, $enregistrement);
681
			self::traiterMotsCle($ligne[C_MOTS_CLES_TEXTE], $cel->id_utilisateur, $enregistrement);
646
		}
682
		}
647
 
683
 
648
		$champs_etendus = self::traiterChampsEtendus($ligne, self::$indexes_colonnes_etendues);
684
		$champs_etendus = self::traiterChampsEtendus($ligne, self::$indexes_colonnes_etendues);
649
		if (!empty($champs_etendus)) {
685
		if (!empty($champs_etendus)) {
650
			$enregistrement['_champs_etendus'] = $champs_etendus;
686
			$enregistrement['_champs_etendus'] = $champs_etendus;
651
		}
687
		}
652
 
688
 
653
		return $enregistrement;
689
		return $enregistrement;
654
	}
690
	}
655
 
691
 
656
	static function traiterChampsEtendus(&$ligne, &$indexes_colonnes_etendues) {
692
	static function traiterChampsEtendus(&$ligne, &$indexes_colonnes_etendues) {
657
		$champs_etendus_indexes = array();
693
		$champs_etendus_indexes = array();
658
		foreach($indexes_colonnes_etendues as $index_num => $label) {
694
		foreach($indexes_colonnes_etendues as $index_num => $label) {
659
			if (isset($ligne[$index_num])) {
695
			if (isset($ligne[$index_num])) {
660
				$champs_etendus_indexes[str_replace(self::$prefixe_colonnes_etendues, '', $label)] = $ligne[$index_num];
696
				$champs_etendus_indexes[str_replace(self::$prefixe_colonnes_etendues, '', $label)] = $ligne[$index_num];
661
			}
697
			}
662
		}
698
		}
663
		return $champs_etendus_indexes;
699
		return $champs_etendus_indexes;
664
	}
700
	}
665
 
701
 
666
	static function traiterImage($str, $id_utilisateur, &$enregistrement) {
702
	static function traiterImage($str, $id_utilisateur, &$enregistrement) {
667
		$liste_images = array_filter(explode('/', $str));
703
		$liste_images = array_filter(explode('/', $str));
668
		array_walk($liste_images, array(__CLASS__, '__anonyme_4'));
704
		array_walk($liste_images, array(__CLASS__, '__anonyme_4'));
669
 
705
 
670
		$nomsOrignalConcat = implode(',', $liste_images);
706
		$nomsOrignalConcat = implode(',', $liste_images);
671
		$requete = 'SELECT id_image, nom_original '.
707
		$requete = 'SELECT id_image, nom_original '.
672
			'FROM cel_images '.
708
			'FROM cel_images '.
673
			"WHERE ce_utilisateur = $id_utilisateur AND nom_original IN ($nomsOrignalConcat) ".
709
			"WHERE ce_utilisateur = $id_utilisateur AND nom_original IN ($nomsOrignalConcat) ".
674
			' -- '.__FILE__.':'.__LINE__;
710
			' -- '.__FILE__.':'.__LINE__;
675
		$resultat = Cel::db()->requeter($requete);
711
		$resultat = Cel::db()->requeter($requete);
676
 
712
 
677
		if ($resultat) {
713
		if ($resultat) {
678
			$enregistrement['_images'] = $resultat;
714
			$enregistrement['_images'] = $resultat;
679
		}
715
		}
680
	}
716
	}
681
 
717
 
682
	static function traiterMotsCle($str, $id_utilisateur, &$enregistrement) {
718
	static function traiterMotsCle($str, $id_utilisateur, &$enregistrement) {
683
		$liste_mots_cle = $liste_mots_cle_recherche = array_map('trim', array_unique(array_filter(explode(',', $str))));
719
		$liste_mots_cle = $liste_mots_cle_recherche = array_map('trim', array_unique(array_filter(explode(',', $str))));
684
		array_walk($liste_mots_cle_recherche, array(__CLASS__, '__anonyme_4'));
720
		array_walk($liste_mots_cle_recherche, array(__CLASS__, '__anonyme_4'));
685
 
721
 
686
		if (self::$gestion_mots_cles == null) {
722
		if (self::$gestion_mots_cles == null) {
687
			$gestion_mots_cles = new GestionMotsCles($this->config, 'obs');
723
			$gestion_mots_cles = new GestionMotsCles($this->config, 'obs');
688
		}
724
		}
689
		$mots_cles_ids = $gestion_mots_cles->obtenirIdsMotClesPourMotsCles($liste_mots_cle, $id_utilisateur);
725
		$mots_cles_ids = $gestion_mots_cles->obtenirIdsMotClesPourMotsCles($liste_mots_cle, $id_utilisateur);
690
		foreach ($mots_cles_ids as $mot_cle) {
726
		foreach ($mots_cles_ids as $mot_cle) {
691
			$resultat[$mot_cle['id_mot_cle']] = $mot_cle['mot_cle'];
727
			$resultat[$mot_cle['id_mot_cle']] = $mot_cle['mot_cle'];
692
		}
728
		}
693
 
729
 
694
		$enregistrement['mots_cles_texte'] = implode(',', $liste_mots_cle);
730
		$enregistrement['mots_cles_texte'] = implode(',', $liste_mots_cle);
695
		$enregistrement['_mots_cle'] = array(
731
		$enregistrement['_mots_cle'] = array(
696
			'existing' => $resultat,
732
			'existing' => $resultat,
697
			'to_insert' => array_diff($liste_mots_cle, $resultat));
733
			'to_insert' => array_diff($liste_mots_cle, $resultat));
698
	}
734
	}
699
 
735
 
700
 
736
 
701
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
737
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
702
	// TODO: PHP 5.3, utiliser date_parse_from_format()
738
	// TODO: PHP 5.3, utiliser date_parse_from_format()
703
	// TODO: parser les heures (cf product-owner)
739
	// TODO: parser les heures (cf product-owner)
704
	// TODO: passer par le timestamp pour s'assurer de la validité
740
	// TODO: passer par le timestamp pour s'assurer de la validité
705
	static function traiterDateObs($date, $ref_ligne) {
741
	static function traiterDateObs($date, $ref_ligne) {
706
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
742
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
707
		// TODO: PHPExcel_Shared_Date::ExcelToPHP()
743
		// TODO: PHPExcel_Shared_Date::ExcelToPHP()
708
		if (is_double($date)) {
744
		if (is_double($date)) {
709
			if ($date > 0) {
745
			if ($date > 0) {
710
				return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
746
				return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
711
			}
747
			}
712
 
748
 
713
			$msg = "ligne «{$ref_ligne}»: Attention: date antérieure à 1970 et format de cellule «DATE» utilisés ensemble";
749
			$msg = "ligne «{$ref_ligne}»: Attention: date antérieure à 1970 et format de cellule «DATE» utilisés ensemble";
714
			trigger_error($msg, E_USER_NOTICE);
750
			trigger_error($msg, E_USER_NOTICE);
715
		} else {
751
		} else {
716
			// attend l'un des formats de
752
			// attend l'un des formats de
717
			// http://www.php.net/manual/fr/datetime.formats.date.php
753
			// http://www.php.net/manual/fr/datetime.formats.date.php
718
			// le plus simple: YYYY/MM/DD (utilisé à l'export), mais DD-MM-YYYY est aussi supporté
754
			// le plus simple: YYYY/MM/DD (utilisé à l'export), mais DD-MM-YYYY est aussi supporté
719
			$matches = NULL;
755
			$matches = NULL;
720
			// et on essaie d'être sympa et supporter aussi DD/MM/YYYY
756
			// et on essaie d'être sympa et supporter aussi DD/MM/YYYY
721
			if (preg_match(';^([0-3]?\d)/([01]\d)/([12]\d\d\d)$;', $date, $matches)) {
757
			if (preg_match(';^([0-3]?\d)/([01]\d)/([12]\d\d\d)$;', $date, $matches)) {
722
				$date = $matches[3] . '/' . $matches[2] . '/' . $matches[1];
758
				$date = $matches[3] . '/' . $matches[2] . '/' . $matches[1];
723
			}
759
			}
724
			$timestamp = strtotime($date);
760
			$timestamp = strtotime($date);
725
			if (! $timestamp || $timestamp > time() + 3600 * 24 * 1) { // une journée d'avance maxi autorisée (décallage horaire ?)
761
			if (! $timestamp || $timestamp > time() + 3600 * 24 * 1) { // une journée d'avance maxi autorisée (décallage horaire ?)
726
				if ($date) {
762
				if ($date) {
727
					$msg = "ligne «{$ref_ligne}»: Attention: date erronée ($date)";
763
					$msg = "ligne «{$ref_ligne}»: Attention: date erronée ($date)";
728
					trigger_error($msg, E_USER_NOTICE);
764
					trigger_error($msg, E_USER_NOTICE);
729
				}
765
				}
730
				return NULL;
766
				return NULL;
731
			}
767
			}
732
			return strftime('%Y-%m-%d 00:00:00', $timestamp);
768
			return strftime('%Y-%m-%d 00:00:00', $timestamp);
733
		}
769
		}
734
	}
770
	}
735
 
771
 
736
	static function identReferentiel($referentiel, $ligne, $ref_ligne) {
772
	static function identReferentiel($referentiel, $ligne, $ref_ligne) {
737
		// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
773
		// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
738
		if (strpos($referentiel, 'bdtfx') !== FALSE) {
774
		if (strpos($referentiel, 'bdtfx') !== FALSE) {
739
			return 'bdtfx'; //:v1.01';
775
			return 'bdtfx'; //:v1.01';
740
		}
776
		}
741
		if (strpos($referentiel, 'bdtxa') !== FALSE) {
777
		if (strpos($referentiel, 'bdtxa') !== FALSE) {
742
			return 'bdtxa'; //:v1.00';
778
			return 'bdtxa'; //:v1.00';
743
		}
779
		}
744
		if (strpos($referentiel, 'bdnff') !== FALSE) {
780
		if (strpos($referentiel, 'bdnff') !== FALSE) {
745
			return 'bdtfx';
781
			return 'bdtfx';
746
		}
782
		}
747
		if (strpos($referentiel, 'isfan') !== FALSE) {
783
		if (strpos($referentiel, 'isfan') !== FALSE) {
748
			return 'isfan'; //:v1.00';
784
			return 'isfan'; //:v1.00';
749
		}
785
		}
750
		if (strpos($referentiel, 'apd') !== FALSE) {
786
		if (strpos($referentiel, 'apd') !== FALSE) {
751
			return 'apd'; //:v1.00';
787
			return 'apd'; //:v1.00';
752
		}
788
		}
753
		if (strpos($referentiel, 'autre') !== FALSE) {
789
		if (strpos($referentiel, 'autre') !== FALSE) {
754
			return 'autre';
790
			return 'autre';
755
		}
791
		}
756
 
792
 
757
		if ($referentiel && isset($ligne[C_NOM_SEL]) && $ligne[C_NOM_SEL]) {
793
		if ($referentiel && isset($ligne[C_NOM_SEL]) && $ligne[C_NOM_SEL]) {
758
			$msg = "ligne «{$ref_ligne}»: Attention: référentiel «{$referentiel}» inconnu";
794
			$msg = "ligne «{$ref_ligne}»: Attention: référentiel «{$referentiel}» inconnu";
759
			trigger_error($msg, E_USER_NOTICE);
795
			trigger_error($msg, E_USER_NOTICE);
760
			return 'autre';
796
			return 'autre';
761
		}
797
		}
762
		return NULL;
798
		return NULL;
763
	}
799
	}
764
 
800
 
765
	static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx', $ref_ligne) {
801
	static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx', $ref_ligne) {
766
		// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
802
		// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
767
		if ($lon && is_string($lon)) {
803
		if ($lon && is_string($lon)) {
768
			$lon = str_replace(',', '.', $lon);
804
			$lon = str_replace(',', '.', $lon);
769
		}
805
		}
770
		if ($lat && is_string($lat)) {
806
		if ($lat && is_string($lat)) {
771
			$lat = str_replace(',', '.', $lat);
807
			$lat = str_replace(',', '.', $lat);
772
		}
808
		}
773
 
809
 
774
		// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
810
		// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
775
		// tout en uniformisant le format de séparateur des décimales (le ".")
811
		// tout en uniformisant le format de séparateur des décimales (le ".")
776
		if ($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) {
812
		if ($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) {
777
			return sprintf('%.5F', $lon);
813
			return sprintf('%.5F', $lon);
778
		}
814
		}
779
		if ($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) {
815
		if ($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) {
780
			return sprintf('%.5F', $lat);
816
			return sprintf('%.5F', $lat);
781
		}
817
		}
782
 
818
 
783
		if ($lon || $lat) {
819
		if ($lon || $lat) {
784
			trigger_error("ligne \"{$ref_ligne}\": " .
820
			trigger_error("ligne \"{$ref_ligne}\": " .
785
				  "Attention: longitude ou latitude erronée",
821
				  "Attention: longitude ou latitude erronée",
786
				  E_USER_NOTICE);
822
				  E_USER_NOTICE);
787
		}
823
		}
788
		return NULL;
824
		return NULL;
789
	}
825
	}
790
 
826
 
791
	/*
827
	/*
792
	  TODO: s'affranchir du webservice pour la détermination du nom scientifique en s'appuyant sur cel_references,
828
	  TODO: s'affranchir du webservice pour la détermination du nom scientifique en s'appuyant sur cel_references,
793
	  pour des questions de performances
829
	  pour des questions de performances
794
	*/
830
	*/
795
	static function traiterEspece($ligne, Array &$espece, &$referentiel, $taxon_info_webservice) {
831
	static function traiterEspece($ligne, Array &$espece, &$referentiel, $taxon_info_webservice) {
796
		if (empty($ligne[C_NOM_SEL])) {
832
		if (empty($ligne[C_NOM_SEL])) {
797
			return;
833
			return;
798
		}
834
		}
799
 
835
 
800
		// nom_sel reste toujours celui de l'utilisateur
836
		// nom_sel reste toujours celui de l'utilisateur
801
		$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
837
		$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
802
 
838
 
803
		// XXX/attention, nous ne devrions pas accepter un référentiel absent !
839
		// XXX/attention, nous ne devrions pas accepter un référentiel absent !
804
		if (!$referentiel) {
840
		if (!$referentiel) {
805
			$referentiel = 'bdtfx';
841
			$referentiel = 'bdtfx';
806
		}
842
		}
807
		$taxon_info_webservice->setReferentiel($referentiel);
843
		$taxon_info_webservice->setReferentiel($referentiel);
808
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
844
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
809
 
845
 
810
		$determ = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax(trim($ligne[C_NOM_SEL]));
846
		$determ = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax(trim($ligne[C_NOM_SEL]));
811
 
847
 
812
		// note: rechercherInfosSurTexteCodeOuNumTax peut ne retourner qu'une seule clef "nom_sel"
848
		// note: rechercherInfosSurTexteCodeOuNumTax peut ne retourner qu'une seule clef "nom_sel"
813
		if (! $determ) {
849
		if (! $determ) {
814
			// on supprime les noms retenus et renvoi tel quel
850
			// on supprime les noms retenus et renvoi tel quel
815
			// on réutilise les define pour les noms d'indexes, tant qu'à faire
851
			// on réutilise les define pour les noms d'indexes, tant qu'à faire
816
			// XXX; tout à NULL sauf C_NOM_SEL ci-dessus ?
852
			// XXX; tout à NULL sauf C_NOM_SEL ci-dessus ?
817
			$espece[C_NOM_SEL_NN] = @$ligne[C_NOM_SEL_NN];
853
			$espece[C_NOM_SEL_NN] = @$ligne[C_NOM_SEL_NN];
818
			$espece[C_NOM_RET] = @$ligne[C_NOM_RET];
854
			$espece[C_NOM_RET] = @$ligne[C_NOM_RET];
819
			$espece[C_NOM_RET_NN] = @$ligne[C_NOM_RET_NN];
855
			$espece[C_NOM_RET_NN] = @$ligne[C_NOM_RET_NN];
820
			$espece[C_NT] = @$ligne[C_NT];
856
			$espece[C_NT] = @$ligne[C_NT];
821
			$espece[C_FAMILLE] = @$ligne[C_FAMILLE];
857
			$espece[C_FAMILLE] = @$ligne[C_FAMILLE];
822
 
858
 
823
			return;
859
			return;
824
		}
860
		}
825
 
861
 
826
		// succès de la détection, mais résultat partiel
862
		// succès de la détection, mais résultat partiel
827
		if (!isset($determ->id)) {
863
		if (!isset($determ->id)) {
828
			$determ = $taxon_info_webservice->effectuerRequeteInfosComplementairesSurNumNom($determ->{"nom_retenu.id"});
864
			$determ = $taxon_info_webservice->effectuerRequeteInfosComplementairesSurNumNom($determ->{"nom_retenu.id"});
829
		}
865
		}
830
 
866
 
831
		// ne devrait jamais arriver !
867
		// ne devrait jamais arriver !
832
		if (!$determ) {
868
		if (!$determ) {
833
			die("erreur critique: " . __FILE__ . ':' . __LINE__);
869
			die("erreur critique: " . __FILE__ . ':' . __LINE__);
834
		}
870
		}
835
 
871
 
836
		// un schéma <ref>:(nt|nn):<num> (ie: bdtfx:nt:8503) a été passé
872
		// un schéma <ref>:(nt|nn):<num> (ie: bdtfx:nt:8503) a été passé
837
		// dans ce cas on met à jour le référentiel avec celui passé dans le champ espèce
873
		// dans ce cas on met à jour le référentiel avec celui passé dans le champ espèce
838
		if (isset($determ->ref)) {
874
		if (isset($determ->ref)) {
839
			$referentiel = $determ->ref;
875
			$referentiel = $determ->ref;
840
		}
876
		}
841
 
877
 
842
		// succès de la détection
878
		// succès de la détection
843
		// nom_sel est remplacé, mais seulement si un motif spécial à été utilisé (bdtfx:nn:4567)
879
		// nom_sel est remplacé, mais seulement si un motif spécial à été utilisé (bdtfx:nn:4567)
844
		if ($taxon_info_webservice->is_notation_spe) {
880
		if ($taxon_info_webservice->is_notation_spe) {
845
			$espece[C_NOM_SEL] = $determ->nom_sci;
881
			$espece[C_NOM_SEL] = $determ->nom_sci;
846
		}
882
		}
847
 
883
 
848
		// écrasement des numéros (nomenclatural, taxonomique) saisis...
884
		// écrasement des numéros (nomenclatural, taxonomique) saisis...
849
		$espece[C_NOM_SEL_NN] = $determ->id;
885
		$espece[C_NOM_SEL_NN] = $determ->id;
850
		$espece[C_NOM_RET] = RechercheInfosTaxonBeta::supprimerBiblio($determ->nom_retenu_complet);
886
		$espece[C_NOM_RET] = RechercheInfosTaxonBeta::supprimerBiblio($determ->nom_retenu_complet);
851
		$espece[C_NOM_RET_NN] = $determ->{"nom_retenu.id"};
887
		$espece[C_NOM_RET_NN] = $determ->{"nom_retenu.id"};
852
		$espece[C_NT] = $determ->num_taxonomique;
888
		$espece[C_NT] = $determ->num_taxonomique;
853
		$espece[C_FAMILLE] = $determ->famille;
889
		$espece[C_FAMILLE] = $determ->famille;
854
		return;
890
		return;
855
	}
891
	}
856
 
892
 
857
	static function detectFromNom($nom) {
893
	static function detectFromNom($nom) {
858
		$r = Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s') ".
894
		$r = Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s') ".
859
			"ORDER BY nom_sci ASC LIMIT 0, 1",
895
			"ORDER BY nom_sci ASC LIMIT 0, 1",
860
			Cel::db()->proteger($nom)));
896
			Cel::db()->proteger($nom)));
861
		if ($r) {
897
		if ($r) {
862
			return $r;
898
			return $r;
863
		}
899
		}
864
 
900
 
865
		Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s' OR nom LIKE '%s') ".
901
		Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s' OR nom LIKE '%s') ".
866
			"ORDER BY nom_sci ASC LIMIT 0, 1",
902
			"ORDER BY nom_sci ASC LIMIT 0, 1",
867
			Cel::db()->proteger($nom),
903
			Cel::db()->proteger($nom),
868
			Cel::db()->proteger(str_replace(' ', '% ', $nom))));
904
			Cel::db()->proteger(str_replace(' ', '% ', $nom))));
869
		return $r;
905
		return $r;
870
	}
906
	}
871
 
907
 
872
	static function traiterLocalisation($ligne, Array &$localisation) {
908
	static function traiterLocalisation($ligne, Array &$localisation) {
873
		if (empty($ligne[C_ZONE_GEO])) {
909
		if (empty($ligne[C_ZONE_GEO])) {
874
			$ligne[C_ZONE_GEO] = NULL;
910
			$ligne[C_ZONE_GEO] = NULL;
875
		}
911
		}
876
		if (empty($ligne[C_CE_ZONE_GEO])) {
912
		if (empty($ligne[C_CE_ZONE_GEO])) {
877
			$ligne[C_CE_ZONE_GEO] = NULL;
913
			$ligne[C_CE_ZONE_GEO] = NULL;
878
		}
914
		}
879
 
915
 
880
		$identifiant_commune = trim($ligne[C_ZONE_GEO]);
916
		$identifiant_commune = trim($ligne[C_ZONE_GEO]);
881
		if (!$identifiant_commune) {
917
		if (!$identifiant_commune) {
882
			$departement = trim($ligne[C_CE_ZONE_GEO]);
918
			$departement = trim($ligne[C_CE_ZONE_GEO]);
883
 
919
 
884
			if (strpos($departement, 'INSEE-C:', 0) === 0) {
920
			if (strpos($departement, 'INSEE-C:', 0) === 0) {
885
				$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
921
				$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
886
				if (array_key_exists($localisation[C_CE_ZONE_GEO], self::$cache['geo'])) {
922
				if (array_key_exists($localisation[C_CE_ZONE_GEO], self::$cache['geo'])) {
887
					$localisation[C_ZONE_GEO] = self::$cache['geo'][$localisation[C_CE_ZONE_GEO]];
923
					$localisation[C_ZONE_GEO] = self::$cache['geo'][$localisation[C_CE_ZONE_GEO]];
888
				} else {
924
				} else {
889
					$nom = Cel::db()->requeter(sprintf("SELECT nom FROM cel_zones_geo WHERE code = %s LIMIT 1",
925
					$nom = Cel::db()->requeter(sprintf("SELECT nom FROM cel_zones_geo WHERE code = %s LIMIT 1",
890
						self::quoteNonNull(substr($localisation[C_CE_ZONE_GEO], strlen("INSEE-C:")))));
926
						self::quoteNonNull(substr($localisation[C_CE_ZONE_GEO], strlen("INSEE-C:")))));
891
					if ($nom) {
927
					if ($nom) {
892
						$localisation[C_ZONE_GEO] = $nom[0]['nom'];
928
						$localisation[C_ZONE_GEO] = $nom[0]['nom'];
893
					}
929
					}
894
					self::$cache['geo'][$localisation[C_CE_ZONE_GEO]] = @$nom[0]['nom'];
930
					self::$cache['geo'][$localisation[C_CE_ZONE_GEO]] = @$nom[0]['nom'];
895
				}
931
				}
896
				return;
932
				return;
897
			}
933
			}
898
 
934
 
899
			if (!is_numeric($departement)) {
935
			if (!is_numeric($departement)) {
900
				$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
936
				$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
901
				return;
937
				return;
902
			}
938
			}
903
 
939
 
904
			$cache_attempted = FALSE;
940
			$cache_attempted = FALSE;
905
			if(array_key_exists($departement, self::$cache['geo'])) {
941
			if(array_key_exists($departement, self::$cache['geo'])) {
906
				$cache_attempted = TRUE;
942
				$cache_attempted = TRUE;
907
				if (self::$cache['geo'][$departement][0] && self::$cache['geo'][$departement][1]) {
943
				if (self::$cache['geo'][$departement][0] && self::$cache['geo'][$departement][1]) {
908
					$localisation[C_ZONE_GEO] = self::$cache['geo'][$departement][0];
944
					$localisation[C_ZONE_GEO] = self::$cache['geo'][$departement][0];
909
					$localisation[C_CE_ZONE_GEO] = self::$cache['geo'][$departement][1];
945
					$localisation[C_CE_ZONE_GEO] = self::$cache['geo'][$departement][1];
910
					return;
946
					return;
911
				}
947
				}
912
			}
948
			}
913
 
949
 
914
			$requete = "SELECT DISTINCT nom, CONCAT('INSEE-C:', code) AS code ".
950
			$requete = "SELECT DISTINCT nom, CONCAT('INSEE-C:', code) AS code ".
915
				'FROM cel_zones_geo '.
951
				'FROM cel_zones_geo '.
916
				'WHERE code = %s '.
952
				'WHERE code = %s '.
917
				'LIMIT 1 '.
953
				'LIMIT 1 '.
918
				' -- '.__FILE__.':'.__LINE__;
954
				' -- '.__FILE__.':'.__LINE__;
919
			$resultat_commune = Cel::db()->requeter(sprintf($requete, self::quoteNonNull($departement)));
955
			$resultat_commune = Cel::db()->requeter(sprintf($requete, self::quoteNonNull($departement)));
920
			if (! $cache_attempted && $resultat_commune) {
956
			if (! $cache_attempted && $resultat_commune) {
921
				$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
957
				$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
922
				$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
958
				$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
923
				self::$cache['geo'][$departement] = array($resultat_commune[0]['nom'], $resultat_commune[0]['code']);
959
				self::$cache['geo'][$departement] = array($resultat_commune[0]['nom'], $resultat_commune[0]['code']);
924
				return;
960
				return;
925
			}
961
			}
926
			$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
962
			$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
927
			return;
963
			return;
928
		}
964
		}
929
 
965
 
930
		$select = "SELECT DISTINCT nom, code FROM cel_zones_geo";
966
		$select = "SELECT DISTINCT nom, code FROM cel_zones_geo";
931
 
967
 
932
		if (preg_match('/(.+) \((\d{1,5})\)/', $identifiant_commune, $elements)) {
968
		if (preg_match('/(.+) \((\d{1,5})\)/', $identifiant_commune, $elements)) {
933
			// commune + departement : montpellier (34)
969
			// commune + departement : montpellier (34)
934
			$nom_commune=$elements[1];
970
			$nom_commune=$elements[1];
935
			$code_commune=$elements[2];
971
			$code_commune=$elements[2];
936
			if (strlen($code_commune) <= 2) {
972
			if (strlen($code_commune) <= 2) {
937
				$requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
973
				$requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
938
					$select, self::quoteNonNull($nom_commune),
974
					$select, self::quoteNonNull($nom_commune),
939
					self::quoteNonNull($code_commune.'%'));
975
					self::quoteNonNull($code_commune.'%'));
940
			} else {
976
			} else {
941
				$requete = sprintf("%s WHERE nom = %s AND code = %d",
977
				$requete = sprintf("%s WHERE nom = %s AND code = %d",
942
					$select, self::quoteNonNull($nom_commune),
978
					$select, self::quoteNonNull($nom_commune),
943
					$code_commune);
979
					$code_commune);
944
			}
980
			}
945
		} elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
981
		} elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
946
			// Code insee seul
982
			// Code insee seul
947
			$code_insee_commune=$elements[1];
983
			$code_insee_commune=$elements[1];
948
			$requete = sprintf("%s WHERE code = %s", $select, self::quoteNonNull($code_insee_commune));
984
			$requete = sprintf("%s WHERE code = %s", $select, self::quoteNonNull($code_insee_commune));
949
		} else {
985
		} else {
950
			// Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
986
			// Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
951
			// on prend le risque ici de retourner une mauvaise Commune
987
			// on prend le risque ici de retourner une mauvaise Commune
952
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
988
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
953
			$requete = sprintf("%s WHERE nom LIKE %s", $select, self::quoteNonNull($nom_commune.'%'));
989
			$requete = sprintf("%s WHERE nom LIKE %s", $select, self::quoteNonNull($nom_commune.'%'));
954
		}
990
		}
955
 
991
 
956
		if (array_key_exists($identifiant_commune, self::$cache['geo'])) {
992
		if (array_key_exists($identifiant_commune, self::$cache['geo'])) {
957
			$resultat_commune = self::$cache['geo'][$identifiant_commune];
993
			$resultat_commune = self::$cache['geo'][$identifiant_commune];
958
		} else {
994
		} else {
959
			$resultat_commune = Cel::db()->requeter($requete);
995
			$resultat_commune = Cel::db()->requeter($requete);
960
			self::$cache['geo'][$identifiant_commune] = $resultat_commune;
996
			self::$cache['geo'][$identifiant_commune] = $resultat_commune;
961
		}
997
		}
962
 
998
 
963
		// cas de la commune introuvable dans le référentiel
999
		// cas de la commune introuvable dans le référentiel
964
		// réinitialisation aux valeurs du fichier XLS
1000
		// réinitialisation aux valeurs du fichier XLS
965
		if (! $resultat_commune) {
1001
		if (! $resultat_commune) {
966
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
1002
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
967
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
1003
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
968
		} else {
1004
		} else {
969
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
1005
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
970
			$localisation[C_CE_ZONE_GEO] = "INSEE-C:" . $resultat_commune[0]['code'];
1006
			$localisation[C_CE_ZONE_GEO] = "INSEE-C:" . $resultat_commune[0]['code'];
971
			return;
1007
			return;
972
		}
1008
		}
973
 
1009
 
974
		$departement =& $localisation[C_CE_ZONE_GEO];
1010
		$departement =& $localisation[C_CE_ZONE_GEO];
975
 
1011
 
976
		if (strpos($departement, "INSEE-C:", 0) === 0) {
1012
		if (strpos($departement, "INSEE-C:", 0) === 0) {
977
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
1013
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
978
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
1014
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
979
		}
1015
		}
980
 
1016
 
981
		if (!is_numeric($departement)) {
1017
		if (!is_numeric($departement)) {
982
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
1018
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
983
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
1019
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
984
		}
1020
		}
985
 
1021
 
986
		if (strlen($departement) == 4) {
1022
		if (strlen($departement) == 4) {
987
			$departement = "INSEE-C:0$departement";
1023
			$departement = "INSEE-C:0$departement";
988
		}
1024
		}
989
		if (strlen($departement) == 5) {
1025
		if (strlen($departement) == 5) {
990
			$departement = "INSEE-C:$departement";
1026
			$departement = "INSEE-C:$departement";
991
		}
1027
		}
992
		$departement = trim($departement);
1028
		$departement = trim($departement);
993
 
1029
 
994
		$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
1030
		$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
995
		$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
1031
		$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
996
	}
1032
	}
997
 
1033
 
998
	public static function stockerChampsEtendus($champs_etendus, $ordre_ids, $config) {
1034
	public static function stockerChampsEtendus($champs_etendus, $ordre_ids, $config) {
999
		// singleton du pauvre mais l'export est suffisamment inefficace pour s'en priver
1035
		// singleton du pauvre mais l'export est suffisamment inefficace pour s'en priver
1000
		self::$gestion_champs_etendus = self::$gestion_champs_etendus == null ?
1036
		self::$gestion_champs_etendus = self::$gestion_champs_etendus == null ?
1001
			new GestionChampsEtendus($config, 'obs') :
1037
			new GestionChampsEtendus($config, 'obs') :
1002
			self::$gestion_champs_etendus;
1038
			self::$gestion_champs_etendus;
1003
 
1039
 
1004
		$champs_etendus_obs = array();
1040
		$champs_etendus_obs = array();
1005
		foreach ($champs_etendus as $champ_etendu_a_obs) {
1041
		foreach ($champs_etendus as $champ_etendu_a_obs) {
1006
			$id_obs = $ordre_ids[$champ_etendu_a_obs['ordre']]; // id réel de l'observation correspondant à l'ordre
1042
			$id_obs = $ordre_ids[$champ_etendu_a_obs['ordre']]; // id réel de l'observation correspondant à l'ordre
1007
			foreach ($champ_etendu_a_obs['champs_etendus'] as $label => $champ) {
1043
			foreach ($champ_etendu_a_obs['champs_etendus'] as $label => $champ) {
1008
				// XXX: insère t'on des valeurs vides ?
1044
				// XXX: insère t'on des valeurs vides ?
1009
				$valeur = $champ;
1045
				$valeur = $champ;
1010
				$cle = $label;
1046
				$cle = $label;
1011
 
1047
 
1012
				if (!empty($cle) && !empty($valeur)) {
1048
				if (!empty($cle) && !empty($valeur)) {
1013
					$champ_etendu_a_inserer = new ChampEtendu();
1049
					$champ_etendu_a_inserer = new ChampEtendu();
1014
					$champ_etendu_a_inserer->id = $id_obs;
1050
					$champ_etendu_a_inserer->id = $id_obs;
1015
					$champ_etendu_a_inserer->cle = $cle;
1051
					$champ_etendu_a_inserer->cle = $cle;
1016
					$champ_etendu_a_inserer->valeur = $valeur;
1052
					$champ_etendu_a_inserer->valeur = $valeur;
1017
 
1053
 
1018
					$champs_etendus_obs[] = $champ_etendu_a_inserer;
1054
					$champs_etendus_obs[] = $champ_etendu_a_inserer;
1019
				}
1055
				}
1020
			}
1056
			}
1021
		}
1057
		}
1022
 
1058
 
1023
		self::$gestion_champs_etendus->ajouterParLots($champs_etendus_obs);
1059
		self::$gestion_champs_etendus->ajouterParLots($champs_etendus_obs);
1024
		//TODO: que faire si l'insertion des champs étendus échoue ?
1060
		//TODO: que faire si l'insertion des champs étendus échoue ?
1025
		return count($champs_etendus_obs);
1061
		return count($champs_etendus_obs);
1026
	}
1062
	}
1027
 
1063
 
1028
	/* HELPERS */
1064
	/* HELPERS */
1029
 
1065
 
1030
	// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
1066
	// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
1031
	// XXX; utilisé aussi (temporairement ?) par FormateurGroupeColonne.
1067
	// XXX; utilisé aussi (temporairement ?) par FormateurGroupeColonne.
1032
	static function sortArrayByArray($array, $orderArray) {
1068
	static function sortArrayByArray($array, $orderArray) {
1033
		$ordered = array();
1069
		$ordered = array();
1034
		foreach($orderArray as $key) {
1070
		foreach($orderArray as $key) {
1035
			if (array_key_exists($key, $array)) {
1071
			if (array_key_exists($key, $array)) {
1036
				$ordered[$key] = $array[$key];
1072
				$ordered[$key] = $array[$key];
1037
				unset($array[$key]);
1073
				unset($array[$key]);
1038
			}
1074
			}
1039
		}
1075
		}
1040
		return $ordered + $array;
1076
		return $ordered + $array;
1041
	}
1077
	}
1042
 
1078
 
1043
	// retourne une BBox [N,S,E,O) pour un référentiel donné
1079
	// retourne une BBox [N,S,E,O) pour un référentiel donné
1044
	static function getReferentielBBox($referentiel) {
1080
	static function getReferentielBBox($referentiel) {
1045
		if ($referentiel == 'bdtfx') {
1081
		if ($referentiel == 'bdtfx') {
1046
			return Array(
1082
			return Array(
1047
				'NORD' => 51.2, // Dunkerque
1083
				'NORD' => 51.2, // Dunkerque
1048
				'SUD' => 41.3, // Bonifacio
1084
				'SUD' => 41.3, // Bonifacio
1049
				'EST' => 9.7, // Corse
1085
				'EST' => 9.7, // Corse
1050
				'OUEST' => -5.2); // Ouessan
1086
				'OUEST' => -5.2); // Ouessan
1051
		}
1087
		}
1052
		return FALSE;
1088
		return FALSE;
1053
	}
1089
	}
1054
 
1090
 
1055
	// ces valeurs ne sont pas inséré via les placeholders du PDO::preparedStatement
1091
	// ces valeurs ne sont pas inséré via les placeholders du PDO::preparedStatement
1056
	// et doivent donc être échappées correctement.
1092
	// et doivent donc être échappées correctement.
1057
	public function initialiser_colonnes_statiques() {
1093
	public function initialiser_colonnes_statiques() {
1058
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
1094
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
1059
			array(
1095
			array(
1060
				'ce_utilisateur' => self::quoteNonNull($this->id_utilisateur), // peut-être un hash ou un id
1096
				'ce_utilisateur' => self::quoteNonNull($this->id_utilisateur), // peut-être un hash ou un id
1061
				'prenom_utilisateur' => self::quoteNonNull($this->utilisateur['prenom']),
1097
				'prenom_utilisateur' => self::quoteNonNull($this->utilisateur['prenom']),
1062
				'nom_utilisateur' => self::quoteNonNull($this->utilisateur['nom']),
1098
				'nom_utilisateur' => self::quoteNonNull($this->utilisateur['nom']),
1063
				'courriel_utilisateur' => self::quoteNonNull($this->utilisateur['courriel']),
1099
				'courriel_utilisateur' => self::quoteNonNull($this->utilisateur['courriel']),
1064
			));
1100
			));
1065
	}
1101
	}
1066
 
1102
 
1067
	static function initialiser_pdo_ordered_statements($colonnes_statiques) {
1103
	static function initialiser_pdo_ordered_statements($colonnes_statiques) {
1068
		return Array(
1104
		return Array(
1069
			// insert_ligne_pattern_ordre
1105
			// insert_ligne_pattern_ordre
1070
			sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
1106
			sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
1071
				implode(', ', array_keys($colonnes_statiques)),
1107
				implode(', ', array_keys($colonnes_statiques)),
1072
				implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
1108
				implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
1073
 
1109
 
1074
			// insert_ligne_pattern_ordre
1110
			// insert_ligne_pattern_ordre
1075
			sprintf('(%s, %s ?)',
1111
			sprintf('(%s, %s ?)',
1076
				implode(', ', $colonnes_statiques),
1112
				implode(', ', $colonnes_statiques),
1077
				str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
1113
				str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
1078
		);
1114
		);
1079
	}
1115
	}
1080
 
1116
 
1081
	static function initialiser_pdo_statements($colonnes_statiques) {
1117
	static function initialiser_pdo_statements($colonnes_statiques) {
1082
		return Array(
1118
		return Array(
1083
			// insert_prefix
1119
			// insert_prefix
1084
			sprintf('INSERT INTO cel_obs (%s) VALUES ',
1120
			sprintf('INSERT INTO cel_obs (%s) VALUES ',
1085
				implode(', ', self::$ordre_BDD)),
1121
				implode(', ', self::$ordre_BDD)),
1086
 
1122
 
1087
 
1123
 
1088
			// insert_ligne_pattern, cf: self::$insert_ligne_pattern
1124
			// insert_ligne_pattern, cf: self::$insert_ligne_pattern
1089
			'(' .
1125
			'(' .
1090
			// 3) créé une chaîne de liste de champ à inséré en DB
1126
			// 3) créé une chaîne de liste de champ à inséré en DB
1091
			implode(', ', array_values(
1127
			implode(', ', array_values(
1092
			// 2) garde les valeurs fixes (de $colonnes_statiques),
1128
			// 2) garde les valeurs fixes (de $colonnes_statiques),
1093
			// mais remplace les NULL par des "?"
1129
			// mais remplace les NULL par des "?"
1094
			array_map('__anonyme_5',
1130
			array_map('__anonyme_5',
1095
				  // 1) créé un tableau genre (nom_sel_nn => NULL) depuis self::$ordre_BDD
1131
				  // 1) créé un tableau genre (nom_sel_nn => NULL) depuis self::$ordre_BDD
1096
				  // et écrase certaines valeurs avec $colonnes_statiques (initilisé avec les données utilisateur)
1132
				  // et écrase certaines valeurs avec $colonnes_statiques (initilisé avec les données utilisateur)
1097
				  array_merge(array_map('__anonyme_6', array_flip(self::$ordre_BDD)), $colonnes_statiques
1133
				  array_merge(array_map('__anonyme_6', array_flip(self::$ordre_BDD)), $colonnes_statiques
1098
				  )))) .
1134
				  )))) .
1099
			')'
1135
			')'
1100
		);
1136
		);
1101
	}
1137
	}
1102
 
1138
 
1103
	// équivalent à Bdd->proteger() (qui wrap PDO::quote),
1139
	// équivalent à Bdd->proteger() (qui wrap PDO::quote),
1104
	// sans transformer NULL en ""
1140
	// sans transformer NULL en ""
1105
	static function quoteNonNull($chaine) {
1141
	static function quoteNonNull($chaine) {
1106
		if (is_null($chaine)) {
1142
		if (is_null($chaine)) {
1107
			return 'NULL';
1143
			return 'NULL';
1108
		}
1144
		}
1109
		if (!is_string($chaine) && !is_integer($chaine)) {
1145
		if (!is_string($chaine) && !is_integer($chaine)) {
1110
			die('erreur: ' . __FILE__ . ':' . __LINE__);
1146
			die('erreur: ' . __FILE__ . ':' . __LINE__);
1111
		}
1147
		}
1112
		return Cel::db()->quote($chaine);
1148
		return Cel::db()->quote($chaine);
1113
	}
1149
	}
1114
 
1150
 
1115
	public function erreurs_stock($errno, $errstr) {
1151
	public function erreurs_stock($errno, $errstr) {
1116
		$this->bilan[] = $errstr;
1152
		$this->bilan[] = $errstr;
1117
	}
1153
	}
1118
}
1154
}
1119
?>
1155
?>