Subversion Repositories eFlore/Applications.cel

Rev

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

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