Subversion Repositories eFlore/Applications.cel

Rev

Rev 1914 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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