From 0dab56becb88e8bedb6134740e378b9c923e4c20 Mon Sep 17 00:00:00 2001 From: Matthieu Marcillaud Date: Wed, 31 Mar 2021 22:45:55 +0200 Subject: [PATCH] =?UTF-8?q?Le=20calcul=20des=20donn=C3=A9es=20de=20visites?= =?UTF-8?q?,=20et=20son=20moyen=20de=20les=20r=C3=A9cup=C3=A9rer=20en=20CS?= =?UTF-8?q?V=20est=20modifi=C3=A9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Sur le calcul : On génère un tableau en deux parties : - une entrée "meta" contient des informations sur les caractéristiques des données calculées / envoyées. Notamment, on retourne l'objet, l'id_objet, la durée et l'unité de la durée qui était désiré. On transmet également des informations calculées en conséquence, tel que la date de début et fin des données désirées (start_date, end_date), et la date de la première donnée dans cette plage (first_date). Enfin une clé 'columns' indique les colonnes et labels des colonnes associés retournées. - une entrée "data" contient un tableau des données retournées ; (notamment la date et le nombre de visite associées). Le tableau de données ne retourne plus les dates manquantes : s'il n'y a pas de visite pour une des journées, cette date sera vide. Cela simplifie grandement les calculs ici (mais cela les reporte(ra) côté javascript) ## Sur l'obtention des données : - On ne passe plus par le système "transmettre", mais par des squelettes à la racine (statistiques.csv.html et statistiques.json.html) qui vérifient soit une autorisation, soit une authentification faible (ce qu'il y avait avant justement). Cette authentification faible peut servir à des crons (je suppose) pour collecter régulièrement les données. Le fichier CSV retourne le même contenu qu'avant (au dates manquantes près), mais le fichier JSON quant à lui retourne l'ensemble du tableau calculé (meta et data donc). --- inc/stats_visites_to_array.php | 182 +++++++++++++++------------- prive/transmettre/statistiques.html | 10 -- statistiques.csv.html | 22 ++++ statistiques.json.html | 19 +++ statistiques.json_fonctions.php | 7 ++ 5 files changed, 145 insertions(+), 95 deletions(-) delete mode 100644 prive/transmettre/statistiques.html create mode 100644 statistiques.csv.html create mode 100644 statistiques.json.html create mode 100644 statistiques.json_fonctions.php diff --git a/inc/stats_visites_to_array.php b/inc/stats_visites_to_array.php index 6a9a746..6ac5e5f 100644 --- a/inc/stats_visites_to_array.php +++ b/inc/stats_visites_to_array.php @@ -16,105 +16,117 @@ if (!defined('_ECRIRE_INC_VERSION')) { } include_spip('inc/statistiques'); -// moyenne glissante sur 30 jours -define('MOYENNE_GLISSANTE_JOUR', 30); -// moyenne glissante sur 12 mois -define('MOYENNE_GLISSANTE_MOIS', 12); -function inc_stats_visites_to_array_dist($unite, $duree, $id_article, $options = array()) { +/** + * Retourne les statistiques globales ou d'un objet pour une durée donnée + * + * @param string $unite jour | mois | annee + * @param ?int $duree Combien de jours | mois | annee on prend… + * @param string $objet + * @param string $id_objet + * @return array [date => nb visites] + */ +function inc_stats_visites_to_array_dist($unite, ?int $duree = null, ?string $objet = null, ?int $id_objet = null) { $now = time(); - if (!in_array($unite, array('jour', 'mois'))) { - $unite = 'jour'; + if (!in_array($unite, array('jour', 'mois', 'annee', 'day', 'month', 'year'))) { + $unite = 'day'; + } + if (in_array($unite, ['jour', 'day'])) { + $format_sql = '%Y-%m-%d'; + $format = 'Y-m-d'; + $unite = 'day'; + $duration = 'D'; + } elseif (in_array($unite, ['mois', 'month'])) { + $format_sql = '%Y-%m'; + $format = 'Y-m'; + $unite = 'month'; + $duration = 'M'; + } else { + $format_sql = '%Y'; + $format = 'Y'; + $unite = 'year'; + $duration = 'Y'; + } + if ($duree and $duree < 0) { + $duree = null; } - $serveur = ''; + $serveur = ''; $table = "spip_visites"; + $where = []; $order = "date"; - $where = array(); + + $currentDate = (new \DateTime())->format($format); + $startDate = null; + $endDate = $currentDate; + + if ($duree) { - $where[] = sql_date_proche($order, -$duree, 'day', $serveur); + $where[] = sql_date_proche($order, -$duree, $unite, $serveur); + // sql_date_proche utilise une comparaison stricte. On soustrait 1 jour... + $startDate = (new \DateTime())->sub(new \DateInterval('P' . ($duree - 1) . $duration))->format($format); } - if ($id_article) { - $table = "spip_visites_articles"; - $where[] = "id_article=" . intval($id_article); + if ($objet and $id_objet) { + if ($objet === 'article') { + $table = "spip_visites_articles"; + $where[] = "id_article=" . intval($id_objet); + } else { + // plugin stats objets ? + $trouver_table = charger_fonction('trouver_table', 'base'); + if ($trouver_table('spip_visites_objets')) { + $table = "spip_visites_objets"; + $where[] = "objet=" . table_objet($objet); + $where[] = "id_objet=" . intval($id_objet); + } else { + throw new \Exception('Table spip_visisites_objets not found. You need a plugin for stats outside articles.'); + } + } } + $where = implode(" AND ", $where); - $format = ($unite == 'jour' ? '%Y-%m-%d' : '%Y-%m-01'); - - $res = sql_select("SUM(visites) AS v, DATE_FORMAT($order,'$format') AS d", $table, $where, "d", "d", "", '', - $serveur); - - $format = str_replace('%', '', $format); - $periode = ($unite == 'jour' ? 24 * 3600 : 365 * 24 * 3600 / 12); - $step = intval(round($periode * 1.1, 0)); - $glisse = constant('MOYENNE_GLISSANTE_' . strtoupper($unite)); - moyenne_glissante(); - $data = array(); - $r = sql_fetch($res, $serveur); - if (!$r) { - $r = array('d' => date($format, $now), 'v' => 0); - } - do { - $data[$r['d']] = array('visites' => $r['v'], 'moyenne' => moyenne_glissante($r['v'], $glisse)); - $last = $r['d']; - - // donnee suivante - $r = sql_fetch($res, $serveur); - // si la derniere n'est pas la date courante, l'ajouter - if (!$r and $last != date($format, $now)) { - $r = array('d' => date($format, $now), 'v' => 0); - } - // completer les trous manquants si besoin - if ($r) { - $next = strtotime($last); - $current = strtotime($r['d']); - while (($next += $step) < $current and $d = date($format, $next)) { - if (!isset($data[$d])) { - $data[$d] = array('visites' => 0, 'moyenne' => moyenne_glissante(0, $glisse)); - } - $last = $d; - $next = strtotime($last); - } - } - } while ($r); - - // projection pour la derniere barre : - // mesure courante - // + moyenne au pro rata du temps qui reste - $moyenne = end($data); - $moyenne = prev($data); - $moyenne = ($moyenne and isset($moyenne['moyenne'])) ? $moyenne['moyenne'] : 0; - $data[$last]['moyenne'] = $moyenne; - - // temps restant - $remaining = strtotime(date($format, strtotime(date($format, $now)) + $step)) - $now; - - $prorata = $remaining / $periode; - - // projection - $data[$last]['prevision'] = $data[$last]['visites'] + intval(round($moyenne * $prorata)); - /* - * Compter les fichiers en attente de depouillement dans tmp/visites/ - * pour affiner la prediction. - * A activer dans le mes_options si l'hebergement tient le coup en cas de gros pics de traffic - */ - if (!$id_article and defined('_STATS_COMPTE_EN_ATTENTE') AND _STATS_COMPTE_EN_ATTENTE){ - // eviter un depassement memoire en mesurant un echantillon pour commencer - $n = count(glob(_DIR_RACINE . "tmp/visites/0*")); - if ($n < 10000) { - $n = count(glob(_DIR_RACINE . "tmp/visites/*")); - } else { - $n += count(glob(_DIR_RACINE . "tmp/visites/4*")); - $n += count(glob(_DIR_RACINE . "tmp/visites/8*")); - $n += count(glob(_DIR_RACINE . "tmp/visites/c*")); - $n = 4 * $n; - } - $data[$last]['prevision'] += $n; + $firstDateStat = sql_getfetsel("date", $table, $where, "", "date", "0,1"); + if ($firstDateStat) { + $firstDate = (new \DateTime($firstDateStat))->format($format); + } else { + $firstDate = null; } - return $data; + $data = sql_allfetsel( + "DATE_FORMAT($order,'$format_sql') AS formatted_date, SUM(visites) AS visites", + $table, $where, + "formatted_date", + "formatted_date", + "", + '', + $serveur + ); + $data = array_map(function($d) { + $d['date'] = $d['formatted_date']; + unset($d['formatted_date']); + return $d; + }, $data); + + return [ + 'meta' => [ + 'unite' => $unite, + 'duree' => $duree, + 'objet' => $objet, + 'id_objet' => $id_objet, + 'format_date' => $format, + 'start_date' => $startDate ?? $firstDate, + 'end_date' => $endDate, + 'first_date' => $firstDate, + 'columns' => [ + 'date' => _T('public:date'), + 'visites' => _L('Visites'), + ], + ], + 'data' => array_values($data), + ]; + + return array_values($data); } diff --git a/prive/transmettre/statistiques.html b/prive/transmettre/statistiques.html deleted file mode 100644 index 5be7dd9..0000000 --- a/prive/transmettre/statistiques.html +++ /dev/null @@ -1,10 +0,0 @@ -<:date|trim{':'}|trim:>,<:statistiques:info_visites|trim{':'}|trim:> -[(#CLE|affdate{'Y-m-d'})],[(#VALEUR{visites})] - - diff --git a/statistiques.csv.html b/statistiques.csv.html new file mode 100644 index 0000000..5324476 --- /dev/null +++ b/statistiques.csv.html @@ -0,0 +1,22 @@ + +#CACHE{0} +#HTTP_HEADER{Content-Type: text/csv; charset=#CHARSET} +#HTTP_HEADER{Content-Disposition: attachment; filename=statistiques_#ENV{objet}_#ENV{id_objet}_#ENV{duree,90}.csv} +<:date|trim{':'}|trim:>,<:statistiques:info_visites|trim{':'}|trim:> +[(#VALEUR{date}|affdate{'Y-m-d'})],[(#VALEUR{visites})] + + diff --git a/statistiques.json.html b/statistiques.json.html new file mode 100644 index 0000000..f685ec4 --- /dev/null +++ b/statistiques.json.html @@ -0,0 +1,19 @@ + +#CACHE{0} +#HTTP_HEADER{Content-Type: text/javascript; charset=#CHARSET} +[(#ENV{unite,jour}|transmettre_statistiques_json{#ENV{duree},#ENV{objet},#ENV{id_objet}})] + diff --git a/statistiques.json_fonctions.php b/statistiques.json_fonctions.php new file mode 100644 index 0000000..f82bce9 --- /dev/null +++ b/statistiques.json_fonctions.php @@ -0,0 +1,7 @@ +