* * @category DEL * @package Services * @subpackage Statistiques * @version 0.1 * @author Mathias CHOUET * @author Jean-Pascal MILCENT * @author Aurelien PERONNET * @license GPL v3 * @license CECILL v2 * @copyright 1999-2014 Tela Botanica */ class StatistiquesParAnnee { private $conteneur; private $contexte; private $navigation; private $bdd; private $annee = null; private $type = 'tout'; private $methode = ''; public function __construct(Conteneur $conteneur = null) { $this->conteneur = $conteneur == null ? new Conteneur() : $conteneur; $this->contexte = $conteneur->getContexte(); $this->navigation = $conteneur->getNavigation(); $this->bdd = $this->conteneur->getBdd(); } public function consulter() { $this->intitialiserParametresEtRessources(); $this->verifierPreRequis(); $resultat = new ResultatService(); $resultat->corps = call_user_func(array($this, $this->methode)); return $resultat; } private function intitialiserParametresEtRessources() { $this->type = $this->contexte->getRessource(2) != null ? $this->contexte->getRessource(2) : $this->type; $this->methode = $this->obtenirNomMethode($this->type); $this->annee =(int) $this->contexte->getQS('annee') != null ? intval($this->contexte->getQS('annee')) : null; } private function verifierPreRequis() { $erreurs = array(); if ($this->annee != null && !is_int($this->annee)) { $erreurs[] = "Le paramètre 'annee' doit être un entier."; } if (method_exists($this, $this->obtenirNomMethode($this->type)) === false) { $erreurs[] = "Les stats de type '{$this->type}' n'existent pas."; } if (!empty($erreurs)) { $msg = "Erreur de configuration :\n".implode("\n", $erreurs)."\n\n".Statistiques::getDoc(); throw new Exception($msg, RestServeur::HTTP_CODE_MAUVAISE_REQUETE); } } /** * Ouh la jolie méthode magique !! * @param unknown $mot * @return string */ private function obtenirNomMethode($mot) { $classeNom = 'get'.str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $mot)))); return $classeNom; } /** * Prend en paramêtre un tableau de courriels et retourne après avoir intérogé un service we de l'annuaire * une tableau avec en clé le courriel et en valeur, un tableau associatif : * - nom : le nom de l'utilisateur * - prenom : le prénom de l'utilisateur. * @TODO ne gère pas le pseudo, qui devrait être retourné en lieu et place des nom / prénom s'il est utilisé et renseigné * @param array $courriels un tableau de courriels pour lesquels il faut recherche le prénom et nom. */ protected function recupererUtilisateursNomPrenom(Array $courriels) { // Récupération des données au format Json $service = "utilisateur/prenom-nom-par-courriel/".implode(',', $courriels); $url = sprintf($this->config['chemins']['baseURLServicesAnnuaireTpl'], $service); $json = $this->getDao()->consulter($url); return (array) json_decode($json); } // retourne toutes les stats pour l'année spécifiée private function getTout() { $obsIdentifieesFinAnneePlus = $this->getPourcentageObsIdentifieesFinAnneePlus(); $participants = $this->getParticipants(); return array( 'annee' => $this->annee, 'moyenneObsSansNomParMois' => $this->getMoyenneObsSansNomParMois(), 'moyenneObsIdentifieesParMois' => $this->getMoyenneObsIdentifieesParMois(), 'pourcentageObsIdentifieesEnFinDAnnee' => $this->getPourcentageObsIdentifieesFinAnnee(), 'pourcentageObsIdentifieesEnFinDAnneePlusPlus' => $obsIdentifieesFinAnneePlus['pourcentage'], 'moyenneActionsParJour' => $this->getMoyenneActionsParJour(), 'personnesEnvoyantUnePropositionParMois' => $participants['nombre'] ); } // proxy pour le widget de stats private function getObservations() { return $this->getTout(); } // Retourne le nombre moyen d'observations non identifiées envoyées par mois, pour l'année $annee private function getMoyenneObsSansNomParMois() { $sqlTableTmp = "SELECT COUNT(*) AS compte, ". " CONCAT(YEAR(date_transmission),'-',MONTH(date_transmission)) AS anneemois ". "FROM del_observation ". "WHERE (mots_cles_texte LIKE '%aDeterminer%' ". "OR certitude = 'aDeterminer' ". "OR certitude = 'douteux' ". // Obs n'ayant pas de nom_sel_nn (détermination non choisie parmi le référentiel) "OR nom_sel_nn IS NULL ". "OR nom_sel_nn = 0 ". "OR id_observation IN ({$this->getSqlObsSansNom()}) ". ') '. (($this->annee !== null) ? "AND YEAR(date_transmission) = '{$this->annee}' " : ''). 'GROUP BY anneemois '. 'ORDER BY anneemois DESC '; $requete = "SELECT AVG(parMois.compte) AS moyenne FROM ($sqlTableTmp) AS parMois ". ' -- '.__FILE__.' : '.__LINE__; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0]['moyenne']); } private function getSqlObsSansNom() { $sqlObsSansNom = "SELECT DISTINCT ce_observation ". "FROM del_commentaire ". "WHERE proposition_initiale = 1 ". "AND (nom_sel_nn IS NULL OR nom_sel_nn = '') "; return $sqlObsSansNom; } // Retourne la moyenne par mois sur l'année en cours, des propositions marquées comme "retenues" // dont le dernier vote est dans l'année considérée (comptées en groupant par mois du dernier vote) private function getMoyenneObsIdentifieesParMois() { // Compte et date du dernier vote des propositions marquées comme "retenues" $sqlTableTmp1 = "SELECT COUNT(*), MAX(dcv.date) AS maxdate ". "FROM del_commentaire AS dc ". " LEFT JOIN del_commentaire_vote dcv ON dcv.ce_proposition = dc.id_commentaire ". " WHERE proposition_retenue = 1 ". " GROUP BY dc.id_commentaire ". (($this->annee !== null) ? "HAVING MAX(YEAR(dcv.date)) = '{$this->annee}' " : ''); $sqlTableTmp2 = 'SELECT COUNT(*) AS valideesparmois, '. " CONCAT(YEAR(maxdate), '-', MONTH(maxdate)) AS anneemois ". "FROM ($sqlTableTmp1) AS temp ". "GROUP BY anneemois "; $requete = "SELECT AVG(valideesparmois) AS moyenne FROM ($sqlTableTmp2) AS temp2 ". ' -- '.__FILE__.' : '.__LINE__; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0]['moyenne']); } // Version améliorée mais non optimale (prend en compte les consensus non validés) // @TODO on devrait croiser les IDS pour ne pas prendre en compte les obs validées ou en // consensus, mais qui datent des années précédentes // @ACHTUNG mache pas, dépasse les 100% (voir Wiki) private function getPourcentageObsIdentifieesFinAnneePlus() { // Obs ayant atteint un consensus cette année $requete = "SELECT COUNT(*) AS nombre ". "FROM (SELECT id_observation, id_commentaire, id_vote, nbvotes ". "FROM (SELECT do.id_observation, dc.id_commentaire, dcv.id_vote, COUNT(dcv.id_vote) AS nbvotes ". "FROM del_commentaire AS dc ". " LEFT JOIN del_observation AS do ON (do.id_observation = dc.ce_observation) ". " LEFT JOIN del_commentaire_vote AS dcv ON (dc.id_commentaire = dcv.ce_proposition) ". "AND dcv.valeur = 1 ". "AND dc.proposition_retenue = 0 ". "GROUP BY dc.id_commentaire ". (($this->annee != null) ? " HAVING MAX(YEAR(dcv.date)) = '{$this->annee}' " : ''). " ) AS temp ". "GROUP BY id_observation ". ") AS temp2 ". ' -- '.__FILE__.' : '.__LINE__; $obsEnConsensus = $this->bdd->recupererTous($requete); $oc = intval($obsEnConsensus[0]['nombre']); // Obs ayant une "proposition retenue" cette année $requete = "SELECT COUNT(*) AS nombre ". "FROM (SELECT COUNT(DISTINCT id_observation), MAX(dcv.date) AS maxdate ". "FROM del_commentaire AS dc ". " LEFT JOIN del_commentaire_vote AS dcv ON (dcv.ce_proposition = dc.id_commentaire) ". " LEFT JOIN del_observation AS do ON (do.id_observation = dc.ce_observation) ". "WHERE proposition_retenue = 1 ". (($this->annee != null) ? "AND YEAR(do.date_transmission) = '{$this->annee}' " : ''). "GROUP BY dc.id_commentaire ". (($this->annee != null) ? "HAVING MAX(YEAR(dcv.date)) = '{$this->annee}' " : ''). ") as temp ". ' -- '.__FILE__.' : '.__LINE__; $nbObsValidees = $this->bdd->recupererTous($requete); $ov = intval($nbObsValidees[0]['nombre']); // Nombre d'obs sans nom soumises cette année $requete = "SELECT COUNT(*) AS nombre ". "FROM del_observation ". "WHERE (mots_cles_texte LIKE '%aDeterminer%' ". "OR certitude = 'aDeterminer' ". "OR certitude = 'douteux' ". "OR nom_sel_nn IS NULL ". "OR nom_sel_nn = 0 ". "OR id_observation IN ({$this->getSqlObsSansNom()})". ') '. (($this->annee != null) ? "AND YEAR(date_transmission) = '{$this->annee}' " : ''). ' -- '.__FILE__.' : '.__LINE__; $nbObsSansNom = $this->bdd->recupererTous($requete); $osn = intval($nbObsSansNom[0]['nombre']); return array( 'observationsEnConsensus' => $oc, 'observationsValidees' => $ov, 'observationsSansNom' => $osn, 'pourcentage' => ($osn == 0 ? 0 : round(((($oc + $ov) / $osn) * 100), 2)) ); } private function getPourcentageObsIdentifieesFinAnnee() { $requete = "SELECT ( ". "SELECT COUNT(*) FROM ( ". "SELECT COUNT(DISTINCT id_observation), MAX(dcv.date) AS maxdate ". "FROM del_commentaire AS dc ". " LEFT JOIN del_commentaire_vote AS dcv ON (dcv.ce_proposition = dc.id_commentaire) ". " LEFT JOIN del_observation AS do ON (do.id_observation = dc.ce_observation) ". "WHERE proposition_retenue = 1 ". (($this->annee != null) ? "AND YEAR(do.date_transmission) = '{$this->annee}' " : ''). "GROUP BY dc.id_commentaire ". (($this->annee != null) ? "HAVING MAX(YEAR(dcv.date)) = '{$this->annee}' " : ''). ") AS temp)". " / ". "(SELECT COUNT(*) ". "FROM del_observation ". "WHERE (mots_cles_texte LIKE '%aDeterminer%' ". "OR certitude = 'aDeterminer' ". "OR certitude = 'douteux' ". "OR nom_sel_nn IS NULL ". "OR nom_sel_nn = 0 ". "OR id_observation IN ( ". "SELECT DISTINCT ce_observation ". "FROM del_commentaire ". "WHERE proposition_initiale = 1 ". "AND (nom_sel_nn IS NULL OR nom_sel_nn = '') ". ") ". ") ". (($this->annee != null) ? "AND YEAR(date_transmission) = '{$this->annee}' " : ''). ") * 100 AS pourcentage ". ' -- '.__FILE__.' : '.__LINE__; $resultat = $this->bdd->recupererTous($requete); return floatval($resultat[0]['pourcentage']); } // Retourne la moyenne sur l'année du nombre d'actions (commentaire ou vote) par jour private function getMoyenneActionsParJour() { // nombre de commentaires sur l'année $sqlNbreCommentaires = 'SELECT COUNT(*) FROM del_commentaire '. ($this->annee != null ? "WHERE YEAR(date) = '{$this->annee}' " : ''); // nombre de votes sur l'année $sqlNbreVotes = 'SELECT COUNT(*) FROM del_commentaire_vote '. ($this->annee != null ? "WHERE YEAR(date) = '{$this->annee}' " : ''); // nombre de jours écoulés dans l'année* $sqlNbreJours = "SELECT 365 * (YEAR(now()) - MIN(YEAR(date)) + 1) FROM del_commentaire_vote WHERE YEAR(date) != 0 "; if ($this->annee != null) { $sqlNbreJours = "SELECT IF(YEAR(CURDATE()) = '{$this->annee}', DAYOFYEAR(CURDATE()), 365) "; } // nombre d'actions / nombre de jours $requete = "SELECT ((($sqlNbreCommentaires) + ($sqlNbreVotes)) / ($sqlNbreJours)) AS moyenne ". ' -- '.__FILE__.' : '.__LINE__; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0]['moyenne']); } // Retourne le nombre et la liste des personnes ayant sur l'année une moyenne de participation par mois >= 1 private function getParticipants() { // Faire la moyenne par utilisateur et par mois $requete = "SELECT cal.nbmois, SUM(somme) / cal.nbmois as moyenne, ce_utilisateur, utilisateur_courriel ". "FROM ". // Compter le nombre de participations pour chaque utilisateur à chaque mois de cette année "(SELECT COUNT(*) as somme, CONCAT(YEAR(date),'-',MONTH(date)) AS anneemois, ". "ce_utilisateur, utilisateur_courriel, id_commentaire ". "FROM del_commentaire ". "WHERE ce_proposition = '' ". "AND nom_sel_nn != '' ". "AND nom_sel_nn IS NOT NULL ". (($this->annee != null) ? " AND YEAR(date) = '{$this->annee}' " : ''). "GROUP BY anneemois, ce_utilisateur, utilisateur_courriel) AS ppm, ". // Trouver le nombre de mois différents lors desquels les utilisateurs ont participé, cette année // Pour l'année en cours par ex, retournera 2 si on est en février (voire un au début du mois). "(SELECT COUNT(distinct CONCAT(YEAR(date),'-',MONTH(date))) AS nbmois ". "FROM del_commentaire ". "WHERE ce_proposition = '' ". (($this->annee != null) ? "AND YEAR(date) = '{$this->annee}' " : ''). "AND nom_sel_nn != '' ". "AND nom_sel_nn IS NOT NULL) AS cal ". "GROUP BY ce_utilisateur, utilisateur_courriel ". "HAVING SUM(somme) / cal.nbmois >= 1 ". "ORDER BY moyenne ". ' -- '.__FILE__.' : '.__LINE__; $resultat = $this->bdd->recupererTous($requete); $cpt = count($resultat); $retour = array( 'nombre' => intval($cpt), 'donnees' => $resultat ); return $retour; } /** * Statistiques sur les propositions et les votes * - Nombre de propositions faites (au total et sur les derniers 15 jours) * - Nombre de propositions validées(au total et sur le dernier mois) * - Nombres de votes (au total et sur les derniers 15 jours) */ private function getPropositions() { $nbPropositions = $this->getNbPropositions(); $nbPropositionsValidees = $this->getNbPropositionsValidees(); $votes = $this->getNbVotes(); return array( 'annee' => $this->annee, 'nbPropositionsTotal' => $nbPropositions[0], 'nbPropositions15J' => $nbPropositions[1], 'nbPropositionsValidees' => $nbPropositionsValidees[0], 'nbPropositionsValidees15J' => $nbPropositionsValidees[1], 'nbVotes' => $votes[0], 'nbVotes15J' => $votes[1] ); } /** * @TODO Nombre de proposition faites (au total et sur les derniers 15 jours) * @REFLEXION retourner un array ? Si on demande une année spécifique, comment calculer * l'activité des 15 derniers jours (on met la case à null ?) */ private function getNbPropositionsTotal() { $requete = ""; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0][0]); } /** * @TODO Nombre de proposition validées (au total et sur les derniers 15 jours) * @REFLEXION retourner un array ? Si on demande une année spécifique, comment calculer * l'activité des 15 derniers jours (on met la case à null ?) */ private function getNbPropositionsValidees() { $requete = ""; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0][0]); } /** * @TODO Nombre de votes (au total et sur les derniers 15 jours) * @REFLEXION retourner un array ? Si on demande une année spécifique, comment calculer * l'activité des 15 derniers jours (on met la case à null ?) */ private function getNbVotes() { $requete = ""; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0][0]); } /** * Statistiques sur les utilisateurs d'Identiplante * - Nombre total d'utilisateurs * - Nombre d'utilisateurs réguliers * - Nombre d'utilisateurs prenant part aux votes * - Nombre de participants actifs pour le mois, la semaine, l'année */ private function getUtilisateursIp() { $nbUtilisateursActifs = $this->getNbUtilisateursIpActifs(); return array( 'annee' => $this->annee, 'nbUtilisateursTotal' => $this->getNbUtilisateursIpTotal(), 'nbUtilisateursReguliers' => $this->getNbUtilisateursIpReguliers(), 'nbUtilisateursVotant' => $this->getNbUtilisateursIpVotant(), 'nbUtilisateursActifsAnnee' => $nbUtilisateursActifs['annee'], 'nbUtilisateursActifsMois' => $nbUtilisateursActifs['mois'], 'nbUtilisateursActifsSemaine' => $nbUtilisateursActifs['semaine'] ); } /** * @TODO Nombre total d'utilisateurs d'Identiplante * @REFLEXION est-ce le même nombre que les utilisateurs d'Identiplante ? */ private function getNbUtilisateursIpTotal() { $requete = ""; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0][0]); } /** * @TODO Nombre d'utilisateurs réguliers d'Identiplante * @REFLEXION quelle différence avec getNbUtilisateursIpActifs() ? */ private function getNbUtilisateursIpReguliers() { $requete = ""; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0][0]); } /** * @TODO Nombre d'utilisateurs d'Identiplante prenant part aux votes */ private function getNbUtilisateursIpVotant() { $requete = ""; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0][0]); } /** * @TODO Nombre de participants actifs à Identiplante (pour le mois, la semaine, l'année) * @REFLEXION retourner un array ? Si on demande une année spécifique, comment calculer * l'activité du mois et de la semaine (on met la case à null ?) */ private function getNbUtilisateursIpActifs() { $requete = ""; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0][0]); } /** * @TODO Liste des utilisateurs ayant fait le plus de propositions, et * le plus de propositions retenues (ou ayant atteint un consensus) */ private function getListeMeilleursProposeurs() { $liste = array(); $requete = ""; $resultat = $this->bdd->recupererTous($requete); // Formater liste (récupérer les pseudos des gens dans l'annuaire etc. => voir widget CEL stats) // => faut-il le faire ici ou dans le widget ? :-/ plutôt ici non ? // ... return array( 'annee' => $this->annee, 'listeMeilleursProposeurs' => $liste[0], 'listeMeilleursProposeursAyantEteRetenus' => $liste[1] ); } /** * @TODO Liste des utilisateurs ayant fait le plus de votes, et le plus * de votes sur des propositions retenues (ou ayant atteint un consensus) */ private function getListeMeilleursVoteurs() { $liste = array(); $requete = ""; $resultat = $this->bdd->recupererTous($requete); // Formater liste (récupérer les pseudos des gens dans l'annuaire etc. => voir service CEL stats) // => faut-il le faire ici ou dans le widget ? :-/ plutôt ici non ? // ... return array( 'annee' => $this->annee, 'listeMeilleursVoteurs' => $liste[0], 'listeMeilleursVoteursSurPropositionsRetenues' => $liste[1] ); } /** * @TODO Statistiques sur les images de Pictoflora * @REFLEXION aucune idée de ce qu'on met dedans */ private function getImages() { return array( 'annee' => $this->annee ); } /** * @TODO Statistiques sur les mots-clés de Pictoflora * @REFLEXION aucune idée de ce qu'on met dedans */ private function getTags() { return array( 'annee' => $this->annee ); } /** * @TODO Statistiques sur les utilisateurs de Pictoflora * - Nombre total d'utilisateurs * - Nombre d'utilisateurs réguliers * - Nombre d'utilisateurs prenant part au taggage ? * - Nombre de participants actifs pour le mois, la semaine, l'année */ private function getUtilisateursPf() { $nbUtilisateursActifs = $this->getNbUtilisateursPfActifs(); return array( 'annee' => $this->annee, 'nbUtilisateursTotal' => $this->getNbUtilisateursPfTotal(), 'nbUtilisateursReguliers' => $this->getNbUtilisateursPfReguliers(), 'nbUtilisateursTaggant' => $this->getNbUtilisateursPfTaggant(), 'nbUtilisateursActifsAnnee' => $nbUtilisateursActifs['annee'], 'nbUtilisateursActifsMois' => $nbUtilisateursActifs['mois'], 'nbUtilisateursActifsSemaine' => $nbUtilisateursActifs['semaine'] ); } /** * @TODO Nombre total d'utilisateurs de Pictoflora * @REFLEXION est-ce le même nombre que les utilisateurs d'Identiplante ? */ private function getNbUtilisateursPfTotal() { $requete = ""; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0][0]); } /** * @TODO Nombre d'utilisateurs réguliers de Pictoflora * @REFLEXION quelle différence avec getNbUtilisateursPfActifs() ? */ private function getNbUtilisateursPfReguliers() { $requete = ""; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0][0]); } /** * @TODO Nombre d'utilisateurs de Pictoflora prenant part aux votes */ private function getNbUtilisateursPfTaggant() { $requete = ""; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0][0]); } /** * @TODO Nombre de participants à Pictoflora actifs (pour le mois, la semaine, l'année) * @REFLEXION retourner un array ? Si on demande une année spécifique, comment calculer * l'activité du mois et de la semaine (on met la case à null ?) */ private function getNbUtilisateursPfActifs() { $requete = ""; $resultat = $this->bdd->recupererTous($requete); return intval($resultat[0][0]); } /** * @TODO Liste des mots-clés les plus fréquents */ private function getListeMeilleursTags() { $liste = array(); $requete = ""; $resultat = $this->bdd->recupererTous($requete); // Formater liste // ... return array( 'annee' => $this->annee, 'listeMeilleursTags' => $liste ); } /** * @TODO Liste des utilisateurs ayant ajouté le plus de mots-clés */ private function getListeMeilleursTagueurs() { $liste = array(); $requete = ""; $resultat = $this->bdd->recupererTous($requete); // Formater liste (récupérer les pseudos des gens dans l'annuaire etc. => voir service CEL stats) // => faut-il le faire ici ou dans le widget ? :-/ plutôt ici non ? // ... return array( 'annee' => $this->annee, 'listeMeilleursTagueurs' => $liste ); } }