Subversion Repositories eFlore/Applications.cel

Rev

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

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