Subversion Repositories eFlore/Applications.cel

Rev

Go to most recent revision | Details | 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);
2250 aurelien 111
		$limite_decoupage = $this->calculerNbLignesMaxParFichier(explode(',', $colonnes));
1625 aurelien 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;
2190 mathias 126
		} while($nb_observations > 0);
1625 aurelien 127
 
128
		$this->envoyerJson($url_telechargements);
129
	}
1374 aurelien 130
 
2250 aurelien 131
	private function calculerNbLignesMaxParFichier($colonnes) {
132
		$limite = $this->limite_decoupage_defaut;
133
		// TODO: gérer une réduction du nombre de ligne max différement suivant chaque colonne
134
		// demandée, car elles n'impactent pas toutes le temps de d'export de la même manière.
135
		$facteur_reduction = count($colonnes) <= 2 ? 1 :  pow(0.85, count($colonnes) - 2);
136
 
1402 aurelien 137
		switch($this->format) {
138
			case 'csv':
1625 aurelien 139
				$limite = 20000;
140
				break;
141
			case 'xls':
142
				$limite = 8000;
143
				break;
1660 raphael 144
			case 'pdf':
145
				$limite = 300;
146
				break;
1625 aurelien 147
		}
2250 aurelien 148
 
2251 aurelien 149
		return floor($limite * $facteur_reduction);
1625 aurelien 150
	}
151
 
152
	private function getExport() {
153
		$criteres = $this->traiterParametresAutorises($_GET);
1711 raphael 154
		// ne pas faire de super-requête en cas d'absence de paramètres
155
		// par exemple "format", au minimum, devrait être défini
1715 raphael 156
		if(!$criteres) die('erreur: pas de paramètre reçu');
157
		if(!in_array($this->format, array('pdf','csv','xls'))) die('erreur: format invalide');
1711 raphael 158
 
1625 aurelien 159
		$criteres['transmission'] = 1;
1654 aurelien 160
		if($this->doitEtPeutExporterObsPrivees($criteres)) {
161
			unset($criteres['transmission']);
1755 raphael 162
			$this->id_utilisateur = $criteres['ce_utilisateur'];
1654 aurelien 163
		}
1625 aurelien 164
		$chercheur_observations = new RechercheObservation($this->config);
165
 
1679 raphael 166
		$debut = isset($criteres['debut']) ? intval($criteres['debut']) : 0;
167
		$limite = isset($criteres['limite']) ? intval($criteres['limite']) : 0;
1714 raphael 168
		$groupes = @FormateurGroupeColonne::colGroupsValidation($criteres['colonnes']);
1835 raphael 169
		$groupes .= ',auteur';
170
 
1714 raphael 171
		if(!$groupes) die('erreur: Ne peut identifier les groupes de champs demandés.');
1679 raphael 172
 
1714 raphael 173
 
1679 raphael 174
		if($criteres['obsids']) $criteres['sql_brut'] = sprintf('id_observation IN (%s)',
175
																implode(',', $criteres['obsids']));
1625 aurelien 176
 
177
		unset($criteres['limite']);
178
		unset($criteres['debut']);
179
		unset($criteres['format']);
1654 aurelien 180
		unset($criteres['colonnes']);
1679 raphael 181
		unset($criteres['obsids']);
182
 
183
		$observations = $chercheur_observations->rechercherObservations(null, $criteres, $debut, $limite, TRUE)->get();
1659 aurelien 184
		$ids = array();
185
		foreach($observations as &$obs) {
186
			$ids[] = $obs['id_observation'];
187
		}
188
 
1703 raphael 189
		if($this->format == 'pdf') {
190
			$pdf = $this->convertirEnPdf($observations);
191
			$pdf->pdf->Output('etiquettes.pdf', 'I');
192
			exit;
1662 aurelien 193
		}
194
 
1703 raphael 195
		// cas XLS et CSV: on peut avoir besoin des champs étendus, des noms communs et des champs baseflor:
1715 raphael 196
 
197
		// Obtention des colonnes correspondantes aux groupes de champs
1714 raphael 198
		$colonnes = FormateurGroupeColonne::nomEnsembleVersListeColonnes($groupes);
1703 raphael 199
 
1715 raphael 200
		/*
201
		  Champs étendus et noms communs, si demandés.
202
		  * Pour "nom commun", "preload" retourne NULL, car c'est le cache statique de FormateurGroupeColonne
203
		  qu'il initialise et utilise en interne sans qu'un passage par paramètre dans le contexte de CelWidgetExport
204
		  ne soit nécessaire.
205
		  * Pour les champs étendus, c'est CelWidgetExport::$cache qui est utilisé, aussi bien pour les en-têtes que
206
		  pour les données préchargées, cf self::traiterLigneEtendue()
207
		*/
208
		self::$cache = FormateurGroupeColonne::preload($colonnes, $this, $ids);
1703 raphael 209
 
1659 aurelien 210
    	// TODO: tous les champs étendus et les paramètres supplémentaires devraient être passés en un seul
211
    	// tableau (et chaque formateur csv, xls etc... pourrait également être dans une classe à part)
1625 aurelien 212
		switch($this->format) {
1660 raphael 213
		case 'csv':
1715 raphael 214
			$csv = $this->convertirEnCsv($observations, $colonnes);
1660 raphael 215
			$this->envoyerCsv($csv);
216
			break;
217
		case 'xls':
1715 raphael 218
			$xls = $this->convertirEnXls($observations, $colonnes);
1660 raphael 219
			$this->envoyerXls($xls);
220
			break;
221
		default:
1402 aurelien 222
		}
1374 aurelien 223
	}
224
 
1611 raphael 225
	protected function traiterParametresAutorises(Array $parametres) {
1376 aurelien 226
		$parametres_traites = array();
1402 aurelien 227
		$this->format = (isset($parametres['format']) && $parametres['format'] != '') ? $parametres['format'] : $this->format;
1376 aurelien 228
		foreach($parametres as $cle => $valeur) {
1711 raphael 229
			if(is_string($valeur) && !trim($valeur)) continue;
230
			if(isset($this->parametres_autorises[$cle])) {
1376 aurelien 231
				$parametres_traites[$this->parametres_autorises[$cle]] = $valeur;
232
			}
233
		}
1679 raphael 234
		$parametres_traites['obsids'] = @self::traiterObsIds($parametres['obsids']);
1376 aurelien 235
		return $parametres_traites;
236
	}
237
 
1374 aurelien 238
	private function envoyerCsv($csv) {
239
		header('Content-Type: text/csv; charset=UTF-8');
1408 aurelien 240
		header('Content-Disposition: attachment;filename='.$this->nom_fichier_export.'.csv');
1374 aurelien 241
		echo $csv;
1376 aurelien 242
		exit;
1374 aurelien 243
	}
244
 
1402 aurelien 245
	private function envoyerXls($workbook) {
246
		$workbook->close();
247
		exit;
248
	}
249
 
1715 raphael 250
	private function convertirEnCsv(&$data, $colonnes) {
1374 aurelien 251
		$chemin_temp = "php://temp";
252
		$outstream = fopen($chemin_temp, 'r+');
1690 raphael 253
 
1711 raphael 254
		$intitule_champs = array_merge(FormateurGroupeColonne::getIntitulesColonnes($colonnes));
1703 raphael 255
		// en premier car utilisé génériquement dans getLigneObservation()
1711 raphael 256
		if(isset($colonnes['baseflor'])) {
1703 raphael 257
			$intitule_champs = array_merge($intitule_champs, FormateurGroupeColonne::$baseflor_col);
258
		}
259
		// en second car manuellement appellé plus bas, TODO: utiliser l'API du FormateurGroupeColonne
1715 raphael 260
		if(isset($colonnes['etendu'])) {
261
			$intitule_champs = array_merge($intitule_champs, array_values(self::$cache['etendu']['header']));
262
		}
1690 raphael 263
 
264
		// header
265
		fputcsv($outstream, $intitule_champs, ',', '"');
266
		// lignes
1617 aurelien 267
		foreach($data as &$ligne) {
1692 raphael 268
			$ligne = self::filtrerDonneesSensibles($ligne);
1711 raphael 269
			$ligne = FormateurGroupeColonne::getLigneObservation($ligne, $colonnes, $this);
1374 aurelien 270
			fputcsv($outstream, $ligne, ',', '"');
271
		}
272
		rewind($outstream);
273
		$csv = stream_get_contents($outstream);
274
		fclose($outstream);
275
		return $csv;
276
	}
277
 
1715 raphael 278
	private function convertirEnXls(&$data, $colonnes) {
1402 aurelien 279
		$this->extendSpreadsheetProductor = new SpreadsheetProductor();
280
		$this->extendSpreadsheetProductor->initSpreadsheet();
281
 
282
		$workbook = new Spreadsheet_Excel_Writer();
1804 raphael 283
		// avant la définition du titre de la worksheet !
284
		$workbook->setVersion(8);
285
 
1625 aurelien 286
		$worksheet = $workbook->addWorksheet('Liste');
287
		$workbook->setTempDir($this->config['cel']['chemin_stockage_temp']);
1612 raphael 288
		$worksheet->setInputEncoding('utf-8');
1408 aurelien 289
		$workbook->send($this->nom_fichier_export.'.xls');
1402 aurelien 290
 
291
		$nb_lignes = 1;
1690 raphael 292
 
1711 raphael 293
		$intitule_champs = array_merge(FormateurGroupeColonne::getIntitulesColonnes($colonnes));
1703 raphael 294
		// en premier car utilisé génériquement dans getLigneObservation()
1711 raphael 295
		if(isset($colonnes['baseflor'])) {
1703 raphael 296
			$intitule_champs = array_merge($intitule_champs, FormateurGroupeColonne::$baseflor_col);
297
		}
298
		// en second car manuellement appellé plus bas, TODO: utiliser l'API du FormateurGroupeColonne
1715 raphael 299
		if(isset($colonnes['etendu'])) {
300
			$intitule_champs = array_merge($intitule_champs, array_values(self::$cache['etendu']['header']));
301
		}
1703 raphael 302
 
1690 raphael 303
		// header
304
		$indice = 0;
305
		foreach ($intitule_champs as &$intitule) {
1692 raphael 306
			$worksheet->write(0,$indice++,$intitule);
1690 raphael 307
		}
308
 
1617 aurelien 309
		foreach($data as &$ligne) {
1692 raphael 310
			$ligne = self::filtrerDonneesSensibles($ligne);
1711 raphael 311
			$ligne = FormateurGroupeColonne::getLigneObservation($ligne, $colonnes, $this);
1402 aurelien 312
			$indice = 0;
1617 aurelien 313
			foreach($ligne as &$champ) {
1692 raphael 314
				$worksheet->write($nb_lignes,$indice++,$champ);
1402 aurelien 315
			}
316
			$nb_lignes++;
317
		}
318
		return $workbook;
319
	}
320
 
1662 aurelien 321
	private function convertirEnPdf(&$observations) {
1715 raphael 322
		if(count($observations) > 300) die('erreur: trop de données');
1662 aurelien 323
		//require_once('GenerateurPDF.php');
324
		$pdf = new GenerateurPDF();
325
		$pdf->export($observations);
326
		return $pdf;
327
	}
328
 
1692 raphael 329
	static function filtrerDonneesSensibles($ligne) {
1429 aurelien 330
		if(stripos($ligne['mots_cles_texte'], 'sensible') !== false) {
331
			$ligne['latitude'] = '';
332
			$ligne['longitude'] = '';
333
		}
334
		return $ligne;
335
	}
336
 
1654 aurelien 337
	private function doitEtPeutExporterObsPrivees($criteres) {
338
		return isset($criteres['ce_utilisateur']) &&
339
					$this->peutExporterObsPrivees($criteres['ce_utilisateur']);
340
	}
341
 
342
	private function peutExporterObsPrivees($id_utilisateur) {
343
		$gestion_utilisateur = new User($this->config);
344
		$utilisateur = $gestion_utilisateur->obtenirIdentiteConnectee();
1703 raphael 345
		return ! empty($utilisateur['id_utilisateur']) && $id_utilisateur == $utilisateur['id_utilisateur'];
1654 aurelien 346
	}
1679 raphael 347
 
348
	static function traiterObsIds($range_param) {
349
		if (!isset($range_param)) return NULL;
350
		// trim() car: `POST http://url<<<"range=*"`
351
		if (trim($range_param) == '*') return NULL;
352
		return self::rangeToList(trim($range_param));
353
	}
354
 
355
	/*
356
	 * @param $fieldSets: un range, eg: 1-5,8,32,58-101
357
	 * @return un tableau trié, eg: 1,2,3,4,5,8,32,58,...,101
358
	 * http://stackoverflow.com/questions/7698664/converting-a-range-or-partial-array-in-the-form-3-6-or-3-6-12-into-an-arra
359
	 */
360
	static function rangeToList($in = '') {
361
		$inSets = explode(',', trim($in, ','));
362
		$outSets = array();
363
 
364
		foreach($inSets as $inSet) {
365
			list($start,$end) = explode('-', $inSet . '-' . $inSet);
366
			// ignore les ranges trop importants
367
			if($start > 10000000 || $end > 10000000 || abs($start-$end) > 10000) continue;
368
			$outSets = array_merge($outSets,range($start,$end));
369
		}
370
		$outSets = array_unique($outSets);
371
		$outSets = array_filter($outSets, 'is_numeric');
372
		sort($outSets);
373
		return $outSets;
374
	}
1374 aurelien 375
}
376
?>