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