Subversion Repositories eFlore/Applications.cel

Rev

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

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