Subversion Repositories eFlore/Applications.cel

Rev

Rev 1714 | Rev 1716 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1374 aurelien 1
<?php
2
/**
3
* Service fournissant des informations concernant le CEL au format RSS1, RSS2 ou ATOM.
4
* Encodage en entrée : utf8
5
* Encodage en sortie : utf8
6
* Format du service :
7
* /CelWidgetExport/format
8
* /CelWidgetExport/csv
9
*
10
* Les paramêtres :
11
*  - "start" indique le numéro du premier item à afficher
12
*  - "limit" nombre d'items à afficher
13
*
14
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
15
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
16
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
17
* @version $Id$
18
* @copyright 2012
19
*/
1610 raphael 20
 
21
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(realpath(__FILE__))) . '/lib');
22
// la sortie est binaire (xls), mais OLE n'est pas compatible E_ALL en PHP-5.4
1700 raphael 23
error_reporting(error_reporting() & ~E_STRICT);
1610 raphael 24
require_once("lib/OLE.php");
25
require_once("lib/Spreadsheet/Excel/Writer.php");
26
 
1374 aurelien 27
class CelWidgetExport extends Cel {
28
 
1408 aurelien 29
	private $nom_fichier_export = 'cel_export';
1671 aurelien 30
	// certains paramètres apparaissent plusieurs fois car ils ont des alias
31
	// dans certains widgets
1376 aurelien 32
	private $parametres_autorises = array(
1654 aurelien 33
		'id_utilisateur' => 'ce_utilisateur',
1376 aurelien 34
		'utilisateur' => 'courriel_utilisateur',
35
		'commune' => 'zone_geo',
1671 aurelien 36
		'zone_geo' => 'zone_geo',
1376 aurelien 37
		'dept' => 'departement',
1654 aurelien 38
		'departement' => 'departement',
39
		'lieudit' => 'lieudit',
40
		'station' => 'station',
1376 aurelien 41
		'projet' => 'mots_cles',
1379 aurelien 42
		'num_taxon' => 'nt',
1378 aurelien 43
		'date_debut' => 'date_debut',
1387 aurelien 44
		'date_fin' => 'date_fin',
1625 aurelien 45
		'taxon' => 'taxon',
1654 aurelien 46
		'annee' => 'annee',
47
		'mois' => 'mois',
48
		'jour' => 'jour',
49
		'recherche' => 'recherche',
50
		'id_mots_cles' => 'id_mots_cles',
1662 aurelien 51
		'mots_cles' => 'mots_cles',
1625 aurelien 52
		'debut' => 'debut',
53
		'limite' => 'limite',
1654 aurelien 54
		'format' => 'format',
55
		'colonnes' => 'colonnes',
1679 raphael 56
		'transmission' => 'transmission',
57
		'obsids' => 'obsids',
1376 aurelien 58
	);
1402 aurelien 59
 
1625 aurelien 60
	private $limite_decoupage_defaut = 9000;
61
 
1402 aurelien 62
	private $format = 'csv';
1579 aurelien 63
 
1659 aurelien 64
	public $id_utilisateur = null;
1715 raphael 65
 
66
	// un cache, initialisé par certaines fonctions de préchargement, à la manière
67
	// de ce qui est fait par FormateurGroupeColonne
68
	static $cache = Array();
1659 aurelien 69
 
1579 aurelien 70
	public function getRessource() {
71
		return $this->getElement(array());
72
	}
1374 aurelien 73
 
74
	/**
75
	 * Méthode appelée avec une requête de type GET.
76
	 */
1709 raphael 77
	public function getElement($params = array()) {
1711 raphael 78
		switch(@strtolower($params[0])) {
1709 raphael 79
		case 'calcul':
80
			$this->getCalcul();
81
			break;
1625 aurelien 82
 
1709 raphael 83
		case 'export':
84
			$this->getExport();
85
			break;
86
		default:
1625 aurelien 87
			$this->getExport();
88
		}
89
	}
90
 
91
	private function getCalcul() {
1611 raphael 92
		$criteres = $this->traiterParametresAutorises($_GET);
1654 aurelien 93
 
1374 aurelien 94
		$criteres['transmission'] = 1;
1654 aurelien 95
		if($this->doitEtPeutExporterObsPrivees($criteres)) {
96
			unset($criteres['transmission']);
1659 aurelien 97
			$this->id_utilisateur = $criteres['id_utilisateur'];
1654 aurelien 98
		}
1374 aurelien 99
		$chercheur_observations = new RechercheObservation($this->config);
100
 
1379 aurelien 101
		$numero_page = isset($criteres['debut']) ? $criteres['debut'] : 0;
102
		$limite = isset($criteres['limite']) ? $criteres['limite'] : 0;
1714 raphael 103
		$colonnes = @FormateurGroupeColonne::colGroupsValidation($criteres['colonnes']);
1374 aurelien 104
 
105
		unset($criteres['limite']);
106
		unset($criteres['debut']);
1625 aurelien 107
		unset($criteres['format']);
1654 aurelien 108
		unset($criteres['colonnes']);
1625 aurelien 109
 
110
		$nb_observations = $chercheur_observations->compterObservations(null, $criteres);
111
		$limite_decoupage = $this->calculerNbLignesMaxParFichier();
112
 
113
		$url_telechargements = array();
114
		$intervalle = 0;
115
 
116
		$params_url = $criteres;
117
		unset($params_url['transmission']);
118
		do {
119
			$base_url = $this->config['settings']['baseURLAbsolu'].'CelWidgetExport/export';
120
			$params_url['debut'] = $intervalle;
121
			$params_url['limite'] = $limite_decoupage;
122
			$url_telechargement_fichier = $base_url;
1654 aurelien 123
			$url_telechargements[] = $base_url.'?'.http_build_query($params_url).'&format='.$this->format.'&colonnes='.$colonnes;
1625 aurelien 124
			$intervalle += $limite_decoupage;
125
			$nb_observations -= $limite_decoupage;
126
		} while($nb_observations >= $limite_decoupage);
127
 
128
		$this->envoyerJson($url_telechargements);
129
	}
1374 aurelien 130
 
1625 aurelien 131
	private function calculerNbLignesMaxParFichier() {
132
		$limite = $this->limite_decoupage_defaut;
1402 aurelien 133
		switch($this->format) {
134
			case 'csv':
1625 aurelien 135
				$limite = 20000;
136
				break;
137
			case 'xls':
138
				$limite = 8000;
139
				break;
1660 raphael 140
			case 'pdf':
141
				$limite = 300;
142
				break;
1625 aurelien 143
		}
144
		return $limite;
145
	}
146
 
147
	private function getExport() {
148
		$criteres = $this->traiterParametresAutorises($_GET);
1711 raphael 149
		// ne pas faire de super-requête en cas d'absence de paramètres
150
		// par exemple "format", au minimum, devrait être défini
1715 raphael 151
		if(!$criteres) die('erreur: pas de paramètre reçu');
152
		if(!in_array($this->format, array('pdf','csv','xls'))) die('erreur: format invalide');
1711 raphael 153
 
1625 aurelien 154
		$criteres['transmission'] = 1;
1654 aurelien 155
		if($this->doitEtPeutExporterObsPrivees($criteres)) {
156
			unset($criteres['transmission']);
157
		}
1625 aurelien 158
		$chercheur_observations = new RechercheObservation($this->config);
159
 
1679 raphael 160
		$debut = isset($criteres['debut']) ? intval($criteres['debut']) : 0;
161
		$limite = isset($criteres['limite']) ? intval($criteres['limite']) : 0;
1714 raphael 162
		$groupes = @FormateurGroupeColonne::colGroupsValidation($criteres['colonnes']);
163
		if(!$groupes) die('erreur: Ne peut identifier les groupes de champs demandés.');
1679 raphael 164
 
1714 raphael 165
 
1679 raphael 166
		if($criteres['obsids']) $criteres['sql_brut'] = sprintf('id_observation IN (%s)',
167
																implode(',', $criteres['obsids']));
1625 aurelien 168
 
169
		unset($criteres['limite']);
170
		unset($criteres['debut']);
171
		unset($criteres['format']);
1654 aurelien 172
		unset($criteres['colonnes']);
1679 raphael 173
		unset($criteres['obsids']);
174
 
175
		$observations = $chercheur_observations->rechercherObservations(null, $criteres, $debut, $limite, TRUE)->get();
1659 aurelien 176
		$ids = array();
177
		foreach($observations as &$obs) {
178
			$ids[] = $obs['id_observation'];
179
		}
180
 
1703 raphael 181
		if($this->format == 'pdf') {
182
			$pdf = $this->convertirEnPdf($observations);
183
			$pdf->pdf->Output('etiquettes.pdf', 'I');
184
			exit;
1662 aurelien 185
		}
186
 
1703 raphael 187
		// cas XLS et CSV: on peut avoir besoin des champs étendus, des noms communs et des champs baseflor:
1715 raphael 188
 
189
		// Obtention des colonnes correspondantes aux groupes de champs
1714 raphael 190
		$colonnes = FormateurGroupeColonne::nomEnsembleVersListeColonnes($groupes);
1703 raphael 191
 
1715 raphael 192
		/*
193
		  Champs étendus et noms communs, si demandés.
194
		  * Pour "nom commun", "preload" retourne NULL, car c'est le cache statique de FormateurGroupeColonne
195
		  qu'il initialise et utilise en interne sans qu'un passage par paramètre dans le contexte de CelWidgetExport
196
		  ne soit nécessaire.
197
		  * Pour les champs étendus, c'est CelWidgetExport::$cache qui est utilisé, aussi bien pour les en-têtes que
198
		  pour les données préchargées, cf self::traiterLigneEtendue()
199
		*/
200
		self::$cache = FormateurGroupeColonne::preload($colonnes, $this, $ids);
1703 raphael 201
 
1659 aurelien 202
    	// TODO: tous les champs étendus et les paramètres supplémentaires devraient être passés en un seul
203
    	// tableau (et chaque formateur csv, xls etc... pourrait également être dans une classe à part)
1625 aurelien 204
		switch($this->format) {
1660 raphael 205
		case 'csv':
1715 raphael 206
			$csv = $this->convertirEnCsv($observations, $colonnes);
1660 raphael 207
			$this->envoyerCsv($csv);
208
			break;
209
		case 'xls':
1715 raphael 210
			$xls = $this->convertirEnXls($observations, $colonnes);
1660 raphael 211
			$this->envoyerXls($xls);
212
			break;
213
		default:
1402 aurelien 214
		}
1374 aurelien 215
	}
216
 
1611 raphael 217
	protected function traiterParametresAutorises(Array $parametres) {
1376 aurelien 218
		$parametres_traites = array();
1402 aurelien 219
		$this->format = (isset($parametres['format']) && $parametres['format'] != '') ? $parametres['format'] : $this->format;
1376 aurelien 220
		foreach($parametres as $cle => $valeur) {
1711 raphael 221
			if(is_string($valeur) && !trim($valeur)) continue;
222
			if(isset($this->parametres_autorises[$cle])) {
1376 aurelien 223
				$parametres_traites[$this->parametres_autorises[$cle]] = $valeur;
224
			}
225
		}
1679 raphael 226
		$parametres_traites['obsids'] = @self::traiterObsIds($parametres['obsids']);
1376 aurelien 227
		return $parametres_traites;
228
	}
229
 
1374 aurelien 230
	private function envoyerCsv($csv) {
231
		header('Content-Type: text/csv; charset=UTF-8');
1408 aurelien 232
		header('Content-Disposition: attachment;filename='.$this->nom_fichier_export.'.csv');
1374 aurelien 233
		echo $csv;
1376 aurelien 234
		exit;
1374 aurelien 235
	}
236
 
1402 aurelien 237
	private function envoyerXls($workbook) {
238
		$workbook->close();
239
		exit;
240
	}
241
 
1715 raphael 242
	private function convertirEnCsv(&$data, $colonnes) {
1374 aurelien 243
		$chemin_temp = "php://temp";
244
		$outstream = fopen($chemin_temp, 'r+');
1690 raphael 245
 
1711 raphael 246
		$intitule_champs = array_merge(FormateurGroupeColonne::getIntitulesColonnes($colonnes));
1703 raphael 247
		// en premier car utilisé génériquement dans getLigneObservation()
1711 raphael 248
		if(isset($colonnes['baseflor'])) {
1703 raphael 249
			$intitule_champs = array_merge($intitule_champs, FormateurGroupeColonne::$baseflor_col);
250
		}
251
		// en second car manuellement appellé plus bas, TODO: utiliser l'API du FormateurGroupeColonne
1715 raphael 252
		if(isset($colonnes['etendu'])) {
253
			$intitule_champs = array_merge($intitule_champs, array_values(self::$cache['etendu']['header']));
254
		}
1690 raphael 255
 
256
		// header
257
		fputcsv($outstream, $intitule_champs, ',', '"');
258
		// lignes
1617 aurelien 259
		foreach($data as &$ligne) {
1659 aurelien 260
			$id_obs = $ligne['id_observation'];
1692 raphael 261
			$ligne = self::filtrerDonneesSensibles($ligne);
1711 raphael 262
			$ligne = FormateurGroupeColonne::getLigneObservation($ligne, $colonnes, $this);
1715 raphael 263
			self::traiterLigneEtendue($ligne, @self::$cache['etendu']['data'][$id_obs]);
1374 aurelien 264
			fputcsv($outstream, $ligne, ',', '"');
265
		}
266
		rewind($outstream);
267
		$csv = stream_get_contents($outstream);
268
		fclose($outstream);
269
		return $csv;
270
	}
271
 
1715 raphael 272
	private function convertirEnXls(&$data, $colonnes) {
1402 aurelien 273
		$this->extendSpreadsheetProductor = new SpreadsheetProductor();
274
		$this->extendSpreadsheetProductor->initSpreadsheet();
275
 
276
		$workbook = new Spreadsheet_Excel_Writer();
1625 aurelien 277
		$worksheet = $workbook->addWorksheet('Liste');
278
		$workbook->setTempDir($this->config['cel']['chemin_stockage_temp']);
1612 raphael 279
		$workbook->setVersion(8);
1402 aurelien 280
 
1612 raphael 281
		$worksheet->setInputEncoding('utf-8');
1408 aurelien 282
		$workbook->send($this->nom_fichier_export.'.xls');
1402 aurelien 283
 
284
		$nb_lignes = 1;
1690 raphael 285
 
1711 raphael 286
		$intitule_champs = array_merge(FormateurGroupeColonne::getIntitulesColonnes($colonnes));
1703 raphael 287
		// en premier car utilisé génériquement dans getLigneObservation()
1711 raphael 288
		if(isset($colonnes['baseflor'])) {
1703 raphael 289
			$intitule_champs = array_merge($intitule_champs, FormateurGroupeColonne::$baseflor_col);
290
		}
291
		// en second car manuellement appellé plus bas, TODO: utiliser l'API du FormateurGroupeColonne
1715 raphael 292
		if(isset($colonnes['etendu'])) {
293
			$intitule_champs = array_merge($intitule_champs, array_values(self::$cache['etendu']['header']));
294
		}
1703 raphael 295
 
1690 raphael 296
		// header
297
		$indice = 0;
298
		foreach ($intitule_champs as &$intitule) {
1692 raphael 299
			$worksheet->write(0,$indice++,$intitule);
1690 raphael 300
		}
301
 
1617 aurelien 302
		foreach($data as &$ligne) {
1659 aurelien 303
			$id_obs = $ligne['id_observation'];
1692 raphael 304
			$ligne = self::filtrerDonneesSensibles($ligne);
1711 raphael 305
			$ligne = FormateurGroupeColonne::getLigneObservation($ligne, $colonnes, $this);
1715 raphael 306
			self::traiterLigneEtendue($ligne, @self::$cache['etendu']['data'][$id_obs]);
1402 aurelien 307
			$indice = 0;
1617 aurelien 308
			foreach($ligne as &$champ) {
1692 raphael 309
				$worksheet->write($nb_lignes,$indice++,$champ);
1402 aurelien 310
			}
311
			$nb_lignes++;
312
		}
313
		return $workbook;
314
	}
315
 
1662 aurelien 316
	private function convertirEnPdf(&$observations) {
1715 raphael 317
		if(count($observations) > 300) die('erreur: trop de données');
1662 aurelien 318
		//require_once('GenerateurPDF.php');
319
		$pdf = new GenerateurPDF();
320
		$pdf->export($observations);
321
		return $pdf;
322
	}
323
 
1715 raphael 324
	static function traiterLigneEtendue(&$ligne, $ligne_etendue_aplatie) {
325
		// si header n'est pas défini, aucune observation ne possède de champ étendu
326
		// et nous n'ajoutons ni colonnes, ni valeurs.
327
		if(! isset(self::$cache['etendu']['header'])) return;
1690 raphael 328
 
1715 raphael 329
		$ligne_supp = array_fill(0, count(self::$cache['etendu']['header']), '');
1659 aurelien 330
		$ligne_etendue_fmt = array();
1690 raphael 331
 
1715 raphael 332
		// si, cependant cette seule observation n'a pas de champs étendus,
333
		// nous devons rajouter des blancs (notamment dans le cas ou d'autres
334
		// champs viennent à être ajoutés en aval à l'avenir
335
		// cf: $fonction_dynamique dans FormateurGroupeColonne::GenColInfo()
336
		if(! $ligne_etendue_aplatie) {
337
			$ligne = array_merge($ligne, $ligne_supp);
338
			return;
339
		}
340
 
341
		foreach(self::$cache['etendu']['header'] as $colonne) {
1659 aurelien 342
			if(!isset($ligne_etendue_aplatie[$colonne])) {
343
				$ligne_etendue_fmt[$colonne] = '';
344
			} else {
345
				$ligne_etendue_fmt[$colonne] = $ligne_etendue_aplatie[$colonne];
346
			}
347
		}
1690 raphael 348
 
1715 raphael 349
		// XXX/ array_merge() ?
1690 raphael 350
		$ligne += $ligne_etendue_fmt;
1659 aurelien 351
	}
1703 raphael 352
 
1692 raphael 353
	static function filtrerDonneesSensibles($ligne) {
1429 aurelien 354
		if(stripos($ligne['mots_cles_texte'], 'sensible') !== false) {
355
			$ligne['latitude'] = '';
356
			$ligne['longitude'] = '';
357
		}
358
		return $ligne;
359
	}
360
 
1654 aurelien 361
	private function doitEtPeutExporterObsPrivees($criteres) {
362
		return isset($criteres['ce_utilisateur']) &&
363
					$this->peutExporterObsPrivees($criteres['ce_utilisateur']);
364
	}
365
 
366
	private function peutExporterObsPrivees($id_utilisateur) {
367
		$gestion_utilisateur = new User($this->config);
368
		$utilisateur = $gestion_utilisateur->obtenirIdentiteConnectee();
1703 raphael 369
		return ! empty($utilisateur['id_utilisateur']) && $id_utilisateur == $utilisateur['id_utilisateur'];
1654 aurelien 370
	}
1679 raphael 371
 
372
	static function traiterObsIds($range_param) {
373
		if (!isset($range_param)) return NULL;
374
		// trim() car: `POST http://url<<<"range=*"`
375
		if (trim($range_param) == '*') return NULL;
376
		return self::rangeToList(trim($range_param));
377
	}
378
 
379
	/*
380
	 * @param $fieldSets: un range, eg: 1-5,8,32,58-101
381
	 * @return un tableau trié, eg: 1,2,3,4,5,8,32,58,...,101
382
	 * http://stackoverflow.com/questions/7698664/converting-a-range-or-partial-array-in-the-form-3-6-or-3-6-12-into-an-arra
383
	 */
384
	static function rangeToList($in = '') {
385
		$inSets = explode(',', trim($in, ','));
386
		$outSets = array();
387
 
388
		foreach($inSets as $inSet) {
389
			list($start,$end) = explode('-', $inSet . '-' . $inSet);
390
			// ignore les ranges trop importants
391
			if($start > 10000000 || $end > 10000000 || abs($start-$end) > 10000) continue;
392
			$outSets = array_merge($outSets,range($start,$end));
393
		}
394
		$outSets = array_unique($outSets);
395
		$outSets = array_filter($outSets, 'is_numeric');
396
		sort($outSets);
397
		return $outSets;
398
	}
1374 aurelien 399
}
400
?>