diff --git a/ezmashup/territoires_data.php b/ezmashup/territoires_data.php index 0d90091ba4716b862c8c98eb72e61b3d0be0228a..1921c272f22f5f582bf9b6a8920c78ecb7435d3a 100644 --- a/ezmashup/territoires_data.php +++ b/ezmashup/territoires_data.php @@ -222,10 +222,7 @@ function territoires_data_feed_action_definir_url(string $action, array $feed, s if ($categorie) { $url = parametre_url($url, 'c', $categorie); } - } elseif ( - defined('_DIR_PLUGIN_TERRITOIRES_STAT') - and ($action === 'analyser') - ) { + } elseif ($action === 'analyser') { // Pour une analyse statistique, on renvoie vers une page d'affichage du feed et de ces caractéristiques // techniques et statistiques. $url = parametre_url( diff --git a/lang/territoires_data_fr.php b/lang/territoires_data_fr.php index 85658cdde0800432898a3a168ebdccca803eecda..5b1c8b2fd9af92e557e38644c127ef75d6cc0ce9 100644 --- a/lang/territoires_data_fr.php +++ b/lang/territoires_data_fr.php @@ -9,6 +9,8 @@ $GLOBALS[$GLOBALS['idx_lang']] = array( // B 'bouton_recharger' => 'Recharger la configuration des jeux de données', + 'bouton_territoire_feed_analyser' => 'Analyser', + 'bouton_territoire_feed_modifier' => 'Modifier le jeu de données', // E 'erreur_ecriture_config' => 'Erreur d’écriture du fichier de configuration du feed', @@ -78,6 +80,32 @@ $GLOBALS[$GLOBALS['idx_lang']] = array( 'label_feed_version' => 'Version', 'label_id' => 'Identifiant', 'label_titre' => 'Libellé', + 'label_territoire_feed_id' => 'Identifiant du jeu', + 'label_territoire_feed_credit' => 'Crédits des sources', + 'label_territoire_feed_fournisseur' => 'Fournisseur', + 'label_territoire_feed_update' => 'Publication', + 'label_vue_discretisation' => 'Choix d\'une discrétisation', + 'label_vue_distribution' => 'Analyse de la distribution', + 'label_vue_choroplethe' => 'Représentaiton choroplèthe', + 'label_serie_effectif' => 'effectif', + 'label_serie_min' => 'minimum', + 'label_serie_max' => 'maximum', + 'label_serie_etendue' => 'étendue', + 'label_serie_moyenne' => 'moyenne', + 'label_serie_mediane' => 'médiane', + 'label_serie_quartiles' => 'quartiles', + 'label_serie_q1' => 'Q1', + 'label_serie_q2' => 'Q2', + 'label_serie_q3' => 'Q3', + 'label_serie_variance' => 'variance', + 'label_serie_ecart_type' => 'écart type', + 'label_serie_iq' => 'interquartile', + 'label_serie_cv' => 'variation', + 'label_serie_asymetrie_fisher' => 'asymétrie (Fisher)', + 'label_serie_asymetrie_yule' => 'asymétrie (Yule)', + 'label_serie_asymetrie_pearson' => 'asymétrie (Pearson)', + 'label_serie_kurtosis_fisher' => 'aplatissement (Fisher)', + 'label_serie_kurtosis_pearson' => 'aplatissement (Pearson)', 'legende_extra_format' => 'Affichage des valeurs', 'legende_extra_identite' => 'Identification de la nature de données', 'legende_feed_credit' => 'Licence & Crédits', diff --git a/modeles/graphique.html b/modeles/graphique.html new file mode 100755 index 0000000000000000000000000000000000000000..cca24e0e0c78ff6fc95eb4950ed5c3bc2c3149bc --- /dev/null +++ b/modeles/graphique.html @@ -0,0 +1,132 @@ +[(#REM) + + Modèle pour produire des graphiques de feed avec Chart.js + + Paramètres : + **obligatoires + *recommandés + + <!-- Conteneur --> + - id_graphique : Identifiant unique du graphe, par défaut généré aléatoirement + - *conteneur_class : Classes supplémentaires du conteneur + + <!-- Données et textes --> + - **type : Type de graphique (string) + bar (défaut) | pie | line | horizontalBar | radar | doughnut | polarArea | bubble | scatter | area | mixed + - **classes : Liste des classes de la série avec leur effectif, leurs bornes et d'autres attributs (array) + - *labels : Labels utilisés en abscisse (string|array) + +] +[(#REM) + ============================ + 1) Normaliser les paramètres + ============================ +] +[(#REM) Base ] +#SET{id_graphique, #ENV{id_graphique, #VAL{territoire_feed_graphique_}|uniqid}} +#SET{type, #ENV{type, bar}|trim|strtolower} +#SET{couleur, #ENV{couleur, '#bdc8d0'}} + +#SET{data, #ENV{classes}|array_column{effectif}} +#SET{labels, #VAL{strval}|array_map{#ENV{classes}|array_keys}} +#SET{binf, #ENV{classes}|array_column{binf}} +#SET{bsup, #ENV{classes}|array_column{bsup}} +#SET{tooltip_labels_n, Effectif} +#SET{tooltip_labels_binf, Borne inférieure} +#SET{tooltip_labels_bsup, Borne supérieure} + +[(#REM) Options ] +#SET{options, #ARRAY} + +[(#REM) + ======================== + 3) Affichage du graphique + ======================== +] + +<div + class="spip-chart-wrap[ spip-chart-align-(#ENV{align})][ (#ENV{conteneur_class})]" + style="position:relative; max-width:100%;" +> + <canvas id="#GET{id_graphique}" class="spip-chart-canvas"></canvas> +</div> + +<script> +var jQChartjsLoader; +(function () { + function draw_this_chartjs() { + jQuery(function($){ + var conteneur[_(#GET{id_graphique})] = $("[#(#GET{id_graphique})]"); + var spipChart[_(#GET{id_graphique})] = new Chart(conteneur[_(#GET{id_graphique})], { + type: ['(#GET{type})'], + data: { + labels: [(#GET{labels}|json_encode)], + datasets: [{ + label: '', + data: [(#GET{data}|json_encode)], + backgroundColor: ['(#GET{couleur})'], + borderColor: ['(#GET{couleur})'], + fill: true, + binf: [(#GET{binf}|json_encode)], + bsup: [(#GET{bsup}|json_encode)] + }] + }, + options: { + responsive: true, + title: { + display: true, + text: ['(#ENV{titre})'] + }, + legend: false, + tooltips: { + enabled: true, + callbacks: { + title: function(tooltipItems) { + return ''; + }, + label: function(tooltipItem) { + return ''; + }, + footer: function(tooltipItems) { + let label_n = ['(#GET{tooltip_labels_n}|label_ponctuer)']; + let label_binf = ['(#GET{tooltip_labels_binf}|label_ponctuer)']; + let label_bsup = ['(#GET{tooltip_labels_bsup}|label_ponctuer)']; + let texte = ''; + let dataset = tooltipItems[0].dataset; + let index = tooltipItems[0].dataIndex; + let n = dataset.data[index]; + let binf = dataset.binf[index]; + let bsup = dataset.bsup[index]; + texte += label_n + ' ' + n + "\n"; + texte += label_binf + ' ' + binf + "\n"; + texte += label_bsup + ' ' + bsup; + return texte; + } + } + } + } + }); + }); + } + + if (typeof jQuery.ajax === "undefined") { + jQuery(init_chartjs); + } else { + init_chartjs(); + } + function init_chartjs() { + // Charger le javascript une seule fois si plusieurs graphiques + if (typeof Chart === "undefined") { + if (typeof jQChartjsLoader === "undefined") { + jQChartjsLoader = jQuery.ajax({url: '[(#CHEMIN{lib/chartjs/chart.js})]', dataType: 'script', cache: true}); + } + jQChartjsLoader.done(draw_this_chartjs); + } else { + draw_this_chartjs(); + } + } +})(); + +</script> + +#FILTRE{compacte} diff --git a/paquet.xml b/paquet.xml index 9afc2257889f3031e3138b359bb877bf49e4dbc2..96509783cb7e2aeae473b4bf6eae17619c73e910 100644 --- a/paquet.xml +++ b/paquet.xml @@ -17,9 +17,12 @@ <pipeline nom="autoriser" inclure="territoires_data_autorisations.php" /> <pipeline nom="affiche_milieu" inclure="territoires_data_pipelines.php" /> <pipeline nom="post_depeupler_territoire" inclure="territoires_data_pipelines.php" /> + <pipeline nom="feed_action_completer_liste" inclure="territoires_data_pipelines.php" /> - <necessite nom="ezmashup" compatibilite="[1.1.4;]" /> <necessite nom="territoires" compatibilite="[2.0.0;]" /> + <necessite nom="ezmashup" compatibilite="[1.1.4;]" /> <necessite nom="ezcache" compatibilite="[1.5.3;[" /> <necessite nom="cvtupload" compatibilite="[2.1.6;]" /> + <necessite nom="ezmath" compatibilite="[1.0.0-dev;]" /> + <utilise nom="chartjs" compatibilite="[2.2.0;]" /> </paquet> diff --git a/prive/squelettes/contenu/territoire_feed.html b/prive/squelettes/contenu/territoire_feed.html new file mode 100644 index 0000000000000000000000000000000000000000..3832dc84d6cd19a5e82b28bd4734b91a8f5eeae2 --- /dev/null +++ b/prive/squelettes/contenu/territoire_feed.html @@ -0,0 +1,57 @@ +[(#REM) <!-- On rajoute le plugin territoires_data dans le env, car c'est ce plugin qui fournit les feeds -->] +#SET{plugin, territoires_data} +[(#AUTORISER{voir, feed, #ENV{feed_id}, #NULL, #ARRAY{plugin, #GET{plugin}}}|sinon_interdire_acces)] +<BOUCLE_territoire_feed(FEEDS){plugin=#GET{plugin}}{feed_id}{si #ENV{exec}|=={territoire_feed}}> +[(#REM) <!-- Fiche du feed -->] +[(#BOITE_OUVRIR{[ + [(#AUTORISER{modifier, feed, #FEED_ID, #NULL, #ARRAY{plugin, #CHAMP_SQL{plugin}}}) + [(#FEED_URL_ACTION{#CHAMP_SQL{plugin}, editer, #FEED_ID, #SELF|parametre_url{redirect, ''}} + |icone_verticale{<:territoires_data:bouton_territoire_feed_modifier:>, feed-24, edit, #LANG_RIGHT})] + ] + + <h1>(#TITLE)[(#CHEMIN_IMAGE{territoire_feed-24.svg}|balise_img{territoire_feed,cadre-icone})]</h1> + +],simple fiche_objet})] + +<!--affiche_milieu--> + +<div id="wysiwyg"> + <INCLURE{fond=prive/squelettes/inclure/inc-territoire_feed_detail, + plugin, + feed_id, + ajax=wysiwyg, + wysiwyg=1} /> +</div> +#BOITE_FERMER + +[(#REM) <!-- Statistiques standard sur la série (toujours visibles) --> ] +<INCLURE{fond=prive/squelettes/inclure/inc-territoire_feed_statistique, + plugin, + feed_id} /> + +[(#REM) <!-- Onglets de premier niveau pour accéder aux différentes vues du feed -->] +#SET{vue, #ENV{vue, distribution}} +<B_territoire_feed_vues> +<div class="onglets_simple"> + <ul class="vue"> +<BOUCLE_territoire_feed_vues(DATA) {source table, #LISTE{distribution, discretisation}}> + #SET{libelle, #VAL{territoires_data:label_vue_}|concat{#VALEUR}|_T} + [<li> + (#SELF + |parametre_url{vue, #VALEUR} + |lien_ou_expose{ + [(#GET{libelle}|spip_ucfirst)], + #GET{vue}|=={#VALEUR}}) + </li>] +</BOUCLE_territoire_feed_vues> + </ul> + <div class="nettoyeur"></div> +</div> +</B_territoire_feed_vues> + +[(#REM) <!-- Contenu de chaque onglet --> ] +<INCLURE{fond=prive/squelettes/inclure/inc-territoire_feed_#GET{vue}, + plugin, + feed_id, + ajax} /> +</BOUCLE_territoire_feed> diff --git a/prive/squelettes/inclure/inc-territoire_feed_detail.html b/prive/squelettes/inclure/inc-territoire_feed_detail.html new file mode 100644 index 0000000000000000000000000000000000000000..b91f9ddfbe5d20e5033d7c81e3c28eb3475b1b2d --- /dev/null +++ b/prive/squelettes/inclure/inc-territoire_feed_detail.html @@ -0,0 +1,34 @@ +[(#REM) <!-- Détails d'un feed + + Affiche le détail d'un feed, en particulier, les informations sur la cible. + + @param string plugin + Préfixe du plugin utilisateur (obligatoire, non nul). + @param string feed_id + Identifiant du feed (obligatoire, non nul). +-->] +<BOUCLE_feed_details(FEEDS){plugin}{feed_id}> + [(#REM) <!-- Description du feed --> ] + [<div class="champ contenu_descriptif[ (#DESCRIPTION*|strlen|?{'',vide})]"> + <div dir="#LANG_DIR" class="texte">(#DESCRIPTION)</div> + </div>] + + [(#REM) <!-- Liste des crédits des sources --> ] + <B_credits> + <div class="champ contenu_credits"> + <:territoires_data:label_territoire_feed_credit:/> + <BOUCLE_credits(DATA) {source table, #SOURCES_BASIC}> + <dl class="source_feed"> + [<dt><:territoires_data:label_feed_licence|label_ponctuer:></dt> + <dd>(#VALEUR{source/license})</dd>] + [<dt><:territoires_data:label_territoire_feed_update|label_ponctuer:></dt> + <dd>(#VALEUR{source/last_update})</dd>] + [<dt><:territoires_data:label_feed_version|label_ponctuer:></dt> + <dd>(#VALEUR{source/version})</dd>] + [<dt><:territoires_data:label_territoire_feed_fournisseur|label_ponctuer:></dt> + <dd>(#VALEUR{provider/name})</dd>] + </dl> + </BOUCLE_credits> + </div> + </B_credits> +</BOUCLE_feed_details> diff --git a/prive/squelettes/inclure/inc-territoire_feed_discretisation.html b/prive/squelettes/inclure/inc-territoire_feed_discretisation.html new file mode 100644 index 0000000000000000000000000000000000000000..a88d643e3d71007c3d88817f76c5da6fc68408fe --- /dev/null +++ b/prive/squelettes/inclure/inc-territoire_feed_discretisation.html @@ -0,0 +1 @@ +[(#REM) <!-- Liste des indicateur de forme & graphique de base --> ] diff --git a/prive/squelettes/inclure/inc-territoire_feed_distribution.html b/prive/squelettes/inclure/inc-territoire_feed_distribution.html new file mode 100644 index 0000000000000000000000000000000000000000..8c3cd0c7c044a36f82e2c97f1cf4a930b5b94896 --- /dev/null +++ b/prive/squelettes/inclure/inc-territoire_feed_distribution.html @@ -0,0 +1,36 @@ +[(#REM) <!-- Liste indicateurs de form + + Affiche dans 1 tableau les indicateurs de variation, d'asymétrie et d'aplatissement d'un feed + et un graphique représentatif de la distribution (histogramme des effectifs pour un nombre de classes donné) + + @param string plugin + Préfixe du plugin utilisateur (obligatoire, non nul). + @param string feed_id + Identifiant du feed (obligatoire, non nul). +-->] + +[(#REM) <!-- Statistiques descriptives du feed (position, dispersion) --> ] +#SET{statistiques, #SERIE_STATISTIQUES{#ENV{plugin}, #ENV{feed_id}}} + +<div class="statistique_feed"> + [(#REM) <!-- Indicateurs de forme --> ] + <INCLURE{fond=prive/squelettes/inclure/inc-territoire_feed_indicateurs, + plugin, + feed_id, + statistiques=#GET{statistiques}, + indicateurs=#LISTE{cv, asymetrie_fisher, kurtosis_fisher}} /> + + [(#REM) <!-- Histogramme de classes de la série pour visualiser la forme --> ] + #SET{nb_classes, #GET{statistiques/effectif}|<{50}|?{10, 25}} + #SET{discretisation, #SERIE_DISCRETISATION{#ENV{plugin}, #ENV{feed_id}, equivalence, #GET{nb_classes}}} + #SET{data, #GET{discretisation/classes}|array_column{effectif}} + #SET{labels, #GET{discretisation/classes}|array_keys} + #MODELE{ + graphique, + id_graphique=histo, + type=bar, + classes=#GET{discretisation/classes}, + titre='Effectif par classe', + conteneur_class=graphique + } +</div> diff --git a/prive/squelettes/inclure/inc-territoire_feed_indicateurs.html b/prive/squelettes/inclure/inc-territoire_feed_indicateurs.html new file mode 100644 index 0000000000000000000000000000000000000000..15330f1826909f145017184a4596ddab4fffde10 --- /dev/null +++ b/prive/squelettes/inclure/inc-territoire_feed_indicateurs.html @@ -0,0 +1,28 @@ +[(#REM) <!-- Affiche dans un tableau, les indicateurs statistiques demandés. + + @param string plugin + Préfixe du plugin utilisateur (obligatoire, non nul). + @param string feed_id + Identifiant du feed (obligatoire, non nul). + @param string titre + Titre du tableau (caption) ou vide si aucun titre. + @param array indicateurs + Liste des identifiants d'indicateurs. + @param array statistiques + Tableau de toutes les statistiques du feed +-->] +<B_ind_indicateurs> +<div class="liste-objets indicateurs"> + <table class="spip liste"> + [<caption><strong class="caption">(#ENV*{titre})</strong></caption>] + <tbody> +<BOUCLE_ind_indicateurs(DATA){source table, #ENV{indicateurs}}> + <tr> + <td class="titre principale">[(#VAL{territoires_data:label_serie_}|concat{#VALEUR}|_T|spip_ucfirst)]</td> + <td class="valeur">[(#ENV{statistiques/#VALEUR})]</td> + </tr> +</BOUCLE_ind_indicateurs> + </tbody> + </table> +</div> +</B_ind_indicateurs> diff --git a/prive/squelettes/inclure/inc-territoire_feed_statistique.html b/prive/squelettes/inclure/inc-territoire_feed_statistique.html new file mode 100644 index 0000000000000000000000000000000000000000..e6062bff7f992a6bf2718ee5d9c8960eac67e5f3 --- /dev/null +++ b/prive/squelettes/inclure/inc-territoire_feed_statistique.html @@ -0,0 +1,35 @@ +[(#REM) <!-- Liste des statistiques descriptives du feed (position, dispersion) + + Affiche dans 3 tableaux, les différentes statistiques descriptives d'un feed. + + @param string plugin + Préfixe du plugin utilisateur (obligatoire, non nul). + @param string feed_id + Identifiant du feed (obligatoire, non nul). +-->] + +[(#REM) <!-- Statistiques descriptives du feed (position, dispersion) --> ] +#SET{statistiques, #SERIE_STATISTIQUES{#ENV{plugin}, #ENV{feed_id}}} + +<div class="statistique_feed"> + [(#REM) <!-- Indicateurs de base --> ] + <INCLURE{fond=prive/squelettes/inclure/inc-territoire_feed_indicateurs, + plugin, + feed_id, + statistiques=#GET{statistiques}, + indicateurs=#LISTE{effectif, min, max}} /> + + [(#REM) <!-- Indicateurs de position --> ] + <INCLURE{fond=prive/squelettes/inclure/inc-territoire_feed_indicateurs, + plugin, + feed_id, + statistiques=#GET{statistiques}, + indicateurs=#LISTE{moyenne, mediane, q1, q2, q3}} /> + + [(#REM) <!-- Indicateurs de dispersion --> ] + <INCLURE{fond=prive/squelettes/inclure/inc-territoire_feed_indicateurs, + plugin, + feed_id, + statistiques=#GET{statistiques}, + indicateurs=#LISTE{etendue, ecart_type, iq}} /> +</div> diff --git a/prive/squelettes/navigation/territoire_feed.html b/prive/squelettes/navigation/territoire_feed.html new file mode 100644 index 0000000000000000000000000000000000000000..60bb0db0ba845882636834113212f966ece07a35 --- /dev/null +++ b/prive/squelettes/navigation/territoire_feed.html @@ -0,0 +1,45 @@ +#BOITE_OUVRIR{'', info} +<BOUCLE_territoire_feed(FEEDS){feed_id}> +<div class="infos"> + [(#REM) <!-- L'id du feed de territoire -->] + <div class="numero"> + <:territoires_data:label_territoire_feed_id|label_ponctuer:/> + <br>#FEED_ID + </div> + [(#REM) <!-- Le type de territoire concerné -->] + <div class="numero"> + <span class="label"><:territoires_data:label_feed_type_territoire|label_ponctuer:/></span> + [(#CHEMIN_IMAGE{[type_(#TAGS|table_valeur{type})-xx.svg]} + |image_reduire{32,32} + |inserer_attribut{alt, logo_type} + |inserer_attribut{style, 'margin: 0 auto'} + |inserer_attribut{class, 'clearfix icone-type'})] + [(#VAL{territoire:type_}|concat{#TAGS|table_valeur{type}}|_T)] + </div> + [(#REM) <!-- Pays concerné eventuellement -->] + <B_pays> + <div class="numero"> + <BOUCLE_pays(TERRITOIRES){iso_territoire=#TAGS|table_valeur{pays}}> + <span class="label"><:territoire:type_country|label_ponctuer:/></span> + [<br>(#NOM_USAGE)] + </BOUCLE_pays> + </div> + </B_pays> + + [(#REM) <!-- Nature des données (extra) -->] + [(#SET{extra, #MAPPING|table_valeur{static_fields/extra}})] + #SET{config_extra, #CONFIG{territoires_data/extras/#GET{extra}}} + <div class="numero extra"> + <span class="label"><:territoire_extra:titre_extra|spip_ucfirst|label_ponctuer:></span> + <br>[(#GET{config_extra/label}|typo)] + </div> + <dl class="extra"> + <dt><:territoire_extra:champ_extra_format|spip_ucfirst|label_ponctuer:></dt> + <dd>[(#GET{config_extra/format}|territoire_extra_format_traduire)]</dd> + <dt><:territoire_extra:champ_extra_unite|spip_ucfirst|label_ponctuer:></dt> + <dd>[(#GET{config_extra/unite}|territoire_unite_traduire)]</dd> + </dl> + +</div> +</BOUCLE_territoire_feed> +#BOITE_FERMER diff --git a/prive/style_prive_plugin_territoires_data.html b/prive/style_prive_plugin_territoires_data.html index 9d7912254af401c13ab4fbc9642f9c7f67e4d1b8..bb9f91cb5081270d3ff3964113113e3c664e13ef 100644 --- a/prive/style_prive_plugin_territoires_data.html +++ b/prive/style_prive_plugin_territoires_data.html @@ -22,3 +22,22 @@ .liste-objets.territoire_extras tr td {line-height: 1;} .liste-objets.territoire_extras table caption {line-height: 0.4;} .liste-objets.territoire_extras td.extra {text-align: right;} + +/* Boite d'infos */ +#navigation .infos .numero p.prefixe { font-size: 1.3em; text-transform: lowercase; } +#navigation .infos .numero span.label { font-weight: normal; } +#navigation .infos .numero.extra { border-bottom: 0; } +#navigation .infos dl.extra { font-size: 0.9em; display: flex; flex-wrap: wrap; align-items: flex-start; } +#navigation .infos dl.extra dt { flex-basis: 25%; flex-grow: 0; flex-shrink: 0; } +#navigation .infos dl.extra dd { margin-bottom: 0; flex-basis: 75%; flex-grow: 1; flex-shrink: 0; } + +/* Fiche feed - crédits */ +#wysiwyg .contenu_credits dl.source_feed { margin-top: 0.5em; font-size: 0.9em; display: flex; flex-wrap: wrap; align-items: flex-start; } +#wysiwyg .contenu_credits dl.source_feed dt { flex-basis: 15%; flex-grow: 0; flex-shrink: 0; text-align: right; } +#wysiwyg .contenu_credits dl.source_feed dd { margin-bottom: 0; margin-#ENV{left}: 1%; flex-basis: 84%; flex-grow: 1; flex-shrink: 0; } + +/* Fiche feed - onglet statistique */ +#contenu .statistique_feed { display: flex; flex-wrap: wrap; justify-content: space-between; } +#contenu .statistique_feed div.indicateurs { margin-top: 1em; margin-bottom: 0; flex-basis: 32%; flex-grow: 0; flex-shrink: 0; } +#contenu .statistique_feed div.indicateurs .valeur { text-align: #ENV{right}; } +#contenu .statistique_feed div.graphique { margin-top: 1em; margin-bottom: 0; flex-basis: 66%; flex-grow: 0; flex-shrink: 0; } diff --git a/territoires_data_autorisations.php b/territoires_data_autorisations.php index 2cf11d06d95647c3965a101c1f6c4d9e599d6ea9..410c022f4cd6cdb33f1d88ac2d2035bbe1b8de23 100644 --- a/territoires_data_autorisations.php +++ b/territoires_data_autorisations.php @@ -220,3 +220,40 @@ function autoriser_territoireunite_supprimer_dist($faire, $type, $id, $qui, $opt return $autorise; } + +/** + * Autorisation, pour tous les feeds de statistiques, d'analyser les données d'un feed (se rendre vers une page + * d'analyse statistique). Il faut : + * - posséder l'autorisation minimale `ezmashup` + * - fournir un identifiant de feed existant + * - que le feed soit actif et que son type d'extra soit `stat` (Statistiques) + * + * @param string $faire Action demandée : `analyser` + * @param string $type Type d'objet sur lequel appliquer l'action : `feed` + * @param int $id Identifiant de l'objet : celui du feed sur lequel appliquer l'action + * @param null|array|int $qui L'initiateur de l'action: + * - si null on prend alors visiteur_session + * - un id_auteur (on regarde dans la base) + * - un tableau auteur complet, y compris [restreint] + * @param null|array $options Tableau d'options sous forme de tableau associatif : `plugin`, préfixe du plugin utilisateur + * + * @return bool `true`si l'auteur est autorisée à exécuter l'action, `false` sinon. +**/ +function autoriser_feed_analyser_dist($faire, $type, $id, $qui, $options) { + // Initialisation de l'autorisation à non autorisé par défaut. + $autorise = false; + + if ( + autoriser('ezmashup', $type, $id, $qui, $options) + and $id + and is_string($id) + and include_spip('inc/ezmashup_feed') + and ($feed = feed_lire($options['plugin'], $id)) + and ($feed['is_active'] === 'oui') + and ($feed['category'] === 'territory_stat') + ) { + $autorise = true; + } + + return $autorise; +} diff --git a/territoires_data_fonctions.php b/territoires_data_fonctions.php index d09cfa1a1f5c9d2a478b5a912c74cf438e1baf70..009e53c582a665b5cdf6f35dae7636baba289767 100644 --- a/territoires_data_fonctions.php +++ b/territoires_data_fonctions.php @@ -173,3 +173,176 @@ function territoire_unite_traduire(string $unite_id) : string { return $unites[$unite_id]; } + +/** + * Compile la balise `#SERIE_STATISTIQUES` qui les principaux indicateurs statistiques d'une série quantitative. + * La signature de la balise est : `#SERIE_STATISTIQUES{plugin, feed_id[, precision]}`. + * + * @balise + * + * @param Champ $p Pile au niveau de la balise. + * + * @return Champ Pile complétée par le code à générer. + **/ +function balise_SERIE_STATISTIQUES_dist(Champ $p) : Champ { + // Récupération des arguments de la balise. + // -- Identifiant du feed + $plugin = interprete_argument_balise(1, $p); + $plugin = isset($plugin) ? str_replace('\'', '"', $plugin) : '""'; + // -- Identifiant du feed + $id_feed = interprete_argument_balise(2, $p); + $id_feed = isset($id_feed) ? str_replace('\'', '"', $id_feed) : '""'; + // -- Précision des staistiques réelles calculées + $precision = interprete_argument_balise(3, $p); + $precision = $precision ?? '2'; + + // Calcul de la balise + $p->code = "territoire_feed_compiler_statistiques({$plugin}, {$id_feed}, {$precision})"; + + return $p; +} + +function territoire_feed_compiler_statistiques(string $plugin, string $id_feed, ?int $precision = 2) : array { + // Initialisation en statique pour le feed + static $statistiques = []; + + if (!isset($statistiques[$plugin][$id_feed][$precision])) { + $statistiques[$plugin][$id_feed][$precision] = []; + + if ( + // Récupérer la série dans la table des extras de territoire et l'identifiant de l'extra + ($extra = territoire_feed_lire_extra($plugin, $id_feed)) + and ($serie = territoire_feed_lire_serie($plugin, $id_feed)) + ) { + // On acquiert les statistiques + include_spip('inc/ezmath_statistique'); + $statistiques_feed = serie_statistiques($serie); + + // Choix des informations, réorganisation et affichage des valeurs (pas d'erreur on a déjà écarté la série vide). + // -- lecture du format de l'extra et détermination du nombre de décimales + include_spip('inc/config'); + $format = lire_config("territoires_data/extras/{$extra}/format", ''); + $decimales = $format === 'integer' + ? 0 + : (int) str_replace('float', '', $format); + + // -- Entier : effectif + // -- Format de l'extra : min, max, médiane, quartiles, étendue, interquartile + // -- Réel avec la précision fournie : le reste des statistiques + foreach ($statistiques_feed as $_indicateur => $_valeur) { + if ($_indicateur === 'effectif') { + $statistiques_feed[$_indicateur] = number_format((float) $_valeur, 0, ',', ' '); + } elseif (in_array($_indicateur, ['min', 'max', 'mediane', 'q1', 'q2', 'q3', 'etendue', 'iq'])) { + $statistiques_feed[$_indicateur] = number_format((float) $_valeur, $decimales, ',', ' '); + } else { + $statistiques_feed[$_indicateur] = number_format((float) $_valeur, $precision, ',', ' '); + } + } + + // Stockage des statistiques du feed + $statistiques[$plugin][$id_feed][$precision] = $statistiques_feed; + } + } + + return $statistiques[$plugin][$id_feed][$precision]; +} + + +/** + * Compile la balise `#SERIE_DISCRETISATION` qui renvoie les classes et la série discrétisées. + * La signature de la balise est : `#SERIE_STATISTIQUES{plugin, feed_id[, precision]}`. + * + * @balise + * + * @param Champ $p Pile au niveau de la balise. + * + * @return Champ Pile complétée par le code à générer. + **/ +function balise_SERIE_DISCRETISATION_dist(Champ $p) : Champ { + // Récupération des arguments de la balise. + // -- Identifiant du feed + $plugin = interprete_argument_balise(1, $p); + $plugin = isset($plugin) ? str_replace('\'', '"', $plugin) : '""'; + // -- Identifiant du feed + $id_feed = interprete_argument_balise(2, $p); + $id_feed = isset($id_feed) ? str_replace('\'', '"', $id_feed) : '""'; + // -- Méthode de discrétisation + $methode = interprete_argument_balise(3, $p); + $methode = isset($methode) ? str_replace('\'', '"', $methode) : '""'; + // -- Nombre de classes + $nb_classes = interprete_argument_balise(4, $p); + $nb_classes = $nb_classes ?? '5'; + + // Calcul de la balise + $p->code = "territoire_feed_discretiser_serie({$plugin}, {$id_feed}, {$methode}, {$nb_classes})"; + + return $p; +} + +function territoire_feed_discretiser_serie(string $plugin, string $id_feed, ?string $methode = 'equivalence', ?int $nb_classes = 5) : array { + // Initialisation pour le feed + $discretisations = []; + + if ($serie = territoire_feed_lire_serie($plugin, $id_feed)) { + // On acquiert les classes et la série discrétisée + include_spip('inc/ezmath_discretisation'); + $discretisation_feed = serie_discretisation($serie, $methode, $nb_classes); + if (!empty($discretisation_feed['classes'])) { + $discretisations = $discretisation_feed; + + // Ajout des indice donnant le nombre de classe idéal + $discretisation_feed['indices'] = [ + 'hunstberger' => serie_indice_hunstberger($serie), + 'brooks' => serie_indice_brooks($serie), + 'yule' => serie_indice_yule($serie), + 'scott' => serie_indice_scott($serie), + 'diaconis' => serie_indice_diaconis($serie), + ]; + } + } + + return $discretisations; +} + +function territoire_feed_lire_serie(string $plugin, string $id_feed) : array { + // Initialisation en statique pour le feed + static $series = []; + + if (!isset($series[$plugin][$id_feed])) { + $series[$plugin][$id_feed] = []; + + // Récupérer la série dans la table des extras de territoire + $select = ['iso_territoire', 'valeur']; + $where = [ +// 'plugin=' . sql_quote($plugin), // A rajouter plus tard + 'feed_id=' . sql_quote($id_feed), + ]; + $serie = sql_allfetsel($select, 'spip_territoires_extras', $where); + if ($serie) { + $series[$plugin][$id_feed] = array_column($serie, 'valeur', 'iso_territoire'); + } + } + + return $series[$plugin][$id_feed]; +} + +function territoire_feed_lire_extra(string $plugin, string $id_feed) : string { + // Initialisation en statique pour le feed + static $extras = []; + + if (!isset($extras[$plugin][$id_feed])) { + $extras[$plugin][$id_feed] = ''; + + // Récupérer la série dans la table des extras de territoire + $where = [ +// 'plugin=' . sql_quote($plugin), // A rajouter plus tard + 'feed_id=' . sql_quote($id_feed), + ]; + $extra = sql_getfetsel('extra', 'spip_territoires_extras', $where); + if ($extra) { + $extras[$plugin][$id_feed] = $extra; + } + } + + return $extras[$plugin][$id_feed]; +} diff --git a/territoires_data_pipelines.php b/territoires_data_pipelines.php index efa6de9ce4327838909868c08323b1731e90fcf9..59ea07f0e1844b46d2d6930e75b0f82374fbba6b 100644 --- a/territoires_data_pipelines.php +++ b/territoires_data_pipelines.php @@ -129,3 +129,21 @@ function territoires_data_post_depeupler_territoire(array $flux) : array { return $flux; } +function territoires_data_feed_action_completer_liste($flux) { + if ($flux['args']['plugin'] === 'territoires_data') { + $actions = [ + 'analyser' => [ + 'name' => '<:territoires_data:bouton_territoire_feed_analyser:>', + 'icon' => '', + 'type' => 'redirect', + 'auth' => 'analyser', + 'feed' => true, + 'menu' => 'actionner', + 'bord' => true + ], + ]; + $flux['data'] = array_merge($flux['data'], $actions); + } + + return $flux; +}