69eeb4fe96ba77dffe5dcee85f00ea106f30f9f1
[lhc/web/www.git] / www / plugins-dist / statistiques / action / statistiques_archiver.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2016 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 /**
14 * Action d'archivage des statistiques
15 *
16 * @plugin Statistiques pour SPIP
17 * @license GNU/GPL
18 * @package SPIP\Stats\Actions
19 **/
20
21 if (!defined("_ECRIRE_INC_VERSION")) {
22 return;
23 }
24
25
26 if (!defined('STATISTIQUES_ARCHIVER_PAR_MOIS')) {
27 /**
28 * Nombre d'années après quoi on permet de concaténer les statistiques de visites par mois
29 *
30 * Après ce nombre d'années, on peut concaténer les données de visites d'articles par mois
31 * pour prendre moins de place dans la base de données
32 *
33 * @var int Nombre d'années
34 **/
35 define('STATISTIQUES_ARCHIVER_PAR_MOIS', 2);
36 }
37
38 if (!defined('STATISTIQUES_ARCHIVER_PAR_AN')) {
39 /**
40 * Nombre d'années après quoi on permet de concaténer les statistiques de visites par an
41 *
42 * Après ce nombre d'années, on peut concaténer les données de visites d'articles par années
43 * pour prendre moins de place dans la base de données
44 *
45 * @var int Nombre d'années
46 **/
47 define('STATISTIQUES_ARCHIVER_PAR_AN', 5);
48 }
49
50
51 /**
52 * Archiver ou nettoyer des statistiques
53 *
54 * @param string $arg
55 */
56 function action_statistiques_archiver_dist($arg = null) {
57 if (!$arg) {
58 $securiser_action = charger_fonction('securiser_action', 'inc');
59 $arg = $securiser_action();
60 }
61
62 if (!autoriser('webmestre')) {
63 include_spip('inc/minipres');
64 minipres();
65 }
66
67 if (!in_array($arg, array(
68 'archiver_visites_articles',
69 'nettoyer_visites_articles',
70 'nettoyer_referers_articles'
71 ))
72 ) {
73 include_spip('inc/minipres');
74 minipres("Argument non compris");
75 }
76
77 $func = 'statistiques_' . $arg;
78 $func();
79 }
80
81
82 /**
83 * Logguer ces informations importantes.
84 *
85 * @uses spip_log()
86 * @param string $texte
87 **/
88 function statistiques_archiver_log($texte) {
89 spip_log($texte, 'statistiques_archiver.' . _LOG_INFO_IMPORTANTE);
90 }
91
92 /**
93 * Nettoyer des lignes de visites d'articles incorrectes
94 *
95 * Supprime toutes les lignes qui ne font pas partie
96 * d'un article présent en base
97 **/
98 function statistiques_nettoyer_visites_articles() {
99 statistiques_archiver_log("Supprimer les visites d'articles qui n'existent pas dans spip_articles.");
100 $i = sql_delete('spip_visites_articles', 'id_article NOT IN (SELECT id_article FROM spip_articles)');
101 statistiques_archiver_log("Fin de la suppression : $i lignes supprimées");
102 }
103
104 /**
105 * Nettoyer des lignes de referers d'articles incorrectes
106 *
107 * Supprime toutes les lignes qui ne font pas partie
108 * d'un article présent en base
109 **/
110 function statistiques_nettoyer_referers_articles() {
111 statistiques_archiver_log("Supprimer les referers d'articles qui n'existent pas dans spip_articles.");
112 $i = sql_delete('spip_referers_articles', 'id_article NOT IN (SELECT id_article FROM spip_articles)');
113 statistiques_archiver_log("Fin de la suppression : $i lignes supprimées");
114 }
115
116 /**
117 * Archiver les visites d'articles
118 *
119 * @note
120 * Cela peut prendre beaucoup de temps.
121 *
122 * La base de test avait (en 2014) 12.500.000 d'entrées depuis 2005.
123 * Cet archivage réduit à 1.200.000 entrées en réduisant
124 * par mois jusqu'à 2012 inclu et par an jusqu'à 2009 inclu.
125 *
126 * Cela prenait 8 minutes sur ma machine locale
127 * (Intel Core i5-4258U CPU @ 2.40GHz × 4 avec disque SSD)
128 *
129 * @note
130 * On peut suivre l'avancement dans le fichier de log
131 * tail -f tmp/log/statistiques_archiver.log
132 *
133 * @note
134 * On ne peut pas vraiment avec le code actuel de la fonction
135 * appliquer les calculs sur l'ensemble d'un mois car cela
136 * peut facilement surcharger la mémoire de php.
137 *
138 * Du coup, on applique par petit bouts d'abord.
139 *
140 * @uses statistiques_concatener_visites_entre_jours()
141 * @uses statistiques_concatener_visites_par_mois()
142 * @uses statistiques_concatener_visites_par_an()
143 **/
144 function statistiques_archiver_visites_articles() {
145
146 // Tenter de donner du temps au temps
147 @set_time_limit(15 * 60); // 15mn
148
149 $annee_par_mois = date('Y') - STATISTIQUES_ARCHIVER_PAR_MOIS;
150 $annee_par_an = date('Y') - STATISTIQUES_ARCHIVER_PAR_AN;
151
152 $annee_minimum = statistiques_concatener_annee_minimum();
153 if (!$annee_minimum) {
154 return false;
155 }
156
157 if ($annee_minimum > $annee_par_mois) {
158 statistiques_archiver_log("Il n'y a pas de statistiques assez anciennes pour concaténer par mois !");
159 } else {
160 // en plusieurs temps pour éviter trop de mémoire !
161 statistiques_concatener_visites_entre_jours($annee_par_mois, 1, 10);
162 statistiques_concatener_visites_entre_jours($annee_par_mois, 11, 20);
163 statistiques_concatener_visites_entre_jours($annee_par_mois, 21, 31);
164
165 // et on regroupe tout en 1 seul morceau.
166 statistiques_concatener_visites_par_mois($annee_par_mois);
167 }
168
169 if ($annee_minimum > $annee_par_an) {
170 statistiques_archiver_log("Il n'y a pas de statistiques assez anciennes pour concaténer par an !");
171 } else {
172 // et les vieilles années, on regroupe par an directement.
173 statistiques_concatener_visites_par_an($annee_par_an);
174 }
175
176 statistiques_archiver_log("* Optimiser la table spip_visites_articles après les travaux.");
177 sql_optimize('spip_visites_articles');
178 }
179
180 /**
181 * Concatène les statistiques de visites d'articles par mois
182 *
183 * @see statistiques_concatener_visites_entre_jours()
184 *
185 * @param int $annee
186 * On concatène ce qui est avant cette année là.
187 **/
188 function statistiques_concatener_visites_par_mois($annee) {
189 return statistiques_concatener_visites_entre_jours($annee, 1, 31);
190 }
191
192
193 /**
194 * Concatène les statistiques de visites d'articles par portion de mois (entre groupe de jours)
195 *
196 * @param int $annee
197 * On concatène ce qui est avant cette année là.
198 * @param int $debut
199 * Numéro de jour du début de la concaténation, exemple 1.
200 * Le total des visites concaténé sera mis dans ce jour là.
201 * @param int $fin
202 * Numéro de jour de fin de la concaténation, exemple 31.
203 * Toutes les entrées entre le jour $debut+1 et $fin seront supprimées
204 * et concaténées au jour $debut.
205 *
206 **/
207 function statistiques_concatener_visites_entre_jours($annee, $debut, $fin) {
208
209 $annee_minimum = statistiques_concatener_annee_minimum();
210 if (!$annee_minimum) {
211 return false;
212 }
213
214 if ($annee_minimum > $annee) {
215 statistiques_archiver_log("Il n'y a pas de statistiques assez anciennes !");
216
217 return false;
218 }
219
220 // on a besoin pour le champ date d'une écriture sur 2 chiffres.
221 $debut = str_pad($debut, 2, '0', STR_PAD_LEFT);
222 $fin = str_pad($fin, 2, '0', STR_PAD_LEFT);
223
224 statistiques_archiver_log("\nConcaténer les visites d'articles (jours entre $debut et $fin)");
225 statistiques_archiver_log("===========================================================");
226
227 $annees = range($annee_minimum, $annee);
228 $mois = range(1, 12);
229
230 foreach ($annees as $a) {
231 statistiques_archiver_log("\n- Concaténer les visites de l'année : $a");
232
233 foreach ($mois as $m) {
234 $m = str_pad($m, 2, '0', STR_PAD_LEFT);
235 statistiques_concatener_visites_entre_periode("$a-$m-$debut", "$a-$m-$fin");
236 }
237 }
238 }
239
240
241 /**
242 * Retourne la plus petite année des visites d'articles
243 *
244 * @return int|bool
245 * - int : l'année
246 * - false : année non trouvée.
247 **/
248 function statistiques_concatener_annee_minimum() {
249 static $annee_minimum = null;
250
251 // calcul de la plus petite année de statistiques
252 if (is_null($annee_minimum)) {
253 $annee_minimum = sql_getfetsel('YEAR(MIN(date))', 'spip_visites_articles', '', '', '', '0,1');
254 }
255
256 if (!$annee_minimum) {
257 statistiques_archiver_log("Erreur de calcul de la plus petite année de statistiques !");
258
259 return false;
260 }
261
262 return $annee_minimum;
263 }
264
265
266 /**
267 * Concatène les statistiques de visites d'articles par an
268 *
269 * @param int $annee
270 * On concatène ce qui est avant cette année là.
271 *
272 **/
273 function statistiques_concatener_visites_par_an($annee) {
274
275 $annee_minimum = statistiques_concatener_annee_minimum();
276 if (!$annee_minimum) {
277 return false;
278 }
279
280 if ($annee_minimum > $annee) {
281 statistiques_archiver_log("Il n'y a pas de statistiques assez anciennes !");
282
283 return false;
284 }
285
286 statistiques_archiver_log("\nConcaténer les visites d'articles (par an)");
287 statistiques_archiver_log("===========================================================");
288
289 $annees = range($annee_minimum, $annee);
290
291 foreach ($annees as $a) {
292 statistiques_archiver_log("\n- Concaténer les visites de l'année : $a");
293 statistiques_concatener_visites_entre_periode("$a-01-01", "$a-12-31");
294 }
295 }
296
297
298 /**
299 * Concatène les statistiques de visites d'articles entre 2 périodes.
300 *
301 * @param string $date_debut
302 * Date de début tel que '2010-01-01'
303 * @param string $date_fin
304 * Date de fin tel que '2010-12-31'
305 * @return bool
306 * - false : aucune visite sur cette période
307 * - true : il y avait des visites, elles ont été concaténées (ou l'étaient déjà)
308 *
309 **/
310 function statistiques_concatener_visites_entre_periode($date_debut, $date_fin) {
311
312 // récupérer toutes les visites de cette période (année, mois, entre jour début et fin)
313 $visites = sql_allfetsel('id_article, date, visites', 'spip_visites_articles', array(
314 "date >= " . sql_quote($date_debut),
315 "date <= " . sql_quote($date_fin),
316 ));
317
318 if (!$visites) {
319 return false;
320 }
321
322 $liste = $updates = array();
323 $total = 0;
324
325 // - Crée un tableau plus simple (id_article => total des visites de la période) (permettant un array_diff_key facile).
326 // - Calcule au passage le total des visites de la période (pour le log)
327 // - Rempli un autre tableau ($updates) qui indique si cet article doit avoir ses visites concaténées sur cette période,
328 // c'est à dire, si il y a une date qui n'est pas le début de période.
329 // (évite de nombreuses requêtes si l'on exécute plusieurs fois le script)
330 foreach ($visites as $v) {
331 $id_article = $v['id_article'];
332 if (!isset($liste[$id_article])) {
333 $liste[$id_article] = 0;
334 }
335 $liste[$id_article] += $v['visites'];
336 $total += $v['visites'];
337 if ($v['date'] != $date_debut) {
338 $updates[$id_article] = true;
339 }
340 }
341
342 unset($visites);
343
344 $nb_articles = count($liste);
345
346 // juste ceux qui nécessitent une mise à jour (date <> de $debut de période)
347 $liste = array_intersect_key($liste, $updates);
348
349 statistiques_archiver_log("-- du $date_debut au $date_fin : $total visites dans $nb_articles articles");
350
351 if ($liste) {
352
353 // formater pour l'insertion dans la base.
354 $inserts = array();
355 foreach ($liste as $id_article => $visites) {
356 $inserts[] = array(
357 'id_article' => $id_article,
358 'date' => $date_debut,
359 'visites' => $visites,
360 );
361 }
362
363 statistiques_archiver_log("--- concaténer les statistiques de " . count($liste) . " articles");
364
365 // /!\ Attention,
366 // Entre ces 2 requêtes, on peut perdre des données (si timeout ou autre)
367 // Transaction à faire ?
368
369 sql_delete('spip_visites_articles', array(
370 "date >= " . sql_quote($date_debut),
371 "date <= " . sql_quote($date_fin),
372 sql_in('id_article', array_keys($liste)),
373 ));
374
375 sql_insertq_multi('spip_visites_articles', $inserts);
376 }
377
378 unset($liste, $inserts);
379
380 return true;
381 }