/branches/v1.2-lithium/scripts/modules/stats/Stats.php |
---|
New file |
0,0 → 1,32 |
<?php |
/** |
* Script de mise à jour de la table "del_image_stat" |
* @author mathias |
*/ |
class Stats extends DelScript { |
public function executer() { |
$this->majStats(); |
} |
/** |
* Met à jour la table des statistiques en la vidant et en recalculant la moyenne pondérée, |
* le nombre de votes et le nombre de points pour chaque image - utilise "commun". |
* Attention, c'est over-pas-optimisé, ça prend du temps ! |
*/ |
protected function majStats() { |
$requete = "SELECT id_image FROM del_image ORDER BY id_image ASC"; |
$ids = $this->getBdd()->recupererTous($requete); |
$requete = "SELECT id_protocole FROM del_image_protocole ORDER BY id_protocole ASC"; |
$protos = $this->getBdd()->recupererTous($requete); |
foreach ($ids as $id) { |
echo "ID: " . $id['id_image'] . "\n"; |
foreach ($protos as $proto) { |
TelaBotanica\Del\Commun\Stats::updateStats($this->getBdd(), $id['id_image'], $proto['id_protocole']); |
} |
} |
} |
} |
/branches/v1.2-lithium/scripts/modules/adeterminer/Adeterminer.php |
---|
New file |
0,0 → 1,41 |
<?php |
//declare(encoding='UTF-8'); |
/** |
* Exemple de lancement du script : : |
* /opt/lampp/bin/php cli.php coste -a chargerTous |
* |
* @category php 5.2 |
* @package eFlore/Scripts |
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org> |
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org) |
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL |
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL |
* @version $Id$ |
*/ |
class Adeterminer extends DelScript { |
public function executer() { |
try { |
$this->initialiserProjet('adeterminer'); |
// Lancement de l'action demandée |
$cmd = $this->getParametre('a'); |
switch ($cmd) { |
case 'alerteMail' : |
$script = $this->chargerClasse('AlerteMailADeterminer'); |
$script->executer(); |
break; |
default : |
throw new Exception("Erreur : la commande '$cmd' n'existe pas!"); |
} |
} catch (Exception $e) { |
$this->traiterErreur($e->getMessage()); |
} |
} |
private function chargerClasse($classe) { |
require_once $classe.'.php'; |
return new $classe($this->parametres, $this); |
} |
} |
?> |
/branches/v1.2-lithium/scripts/modules/adeterminer/AlerteMailADeterminer.php |
---|
New file |
0,0 → 1,224 |
<?php |
// declare(encoding='UTF-8'); |
/** |
* Le script récupère tous les commentaires et déterminations fait ce jour et envoie un mail |
* aux auteurs des observations récapitulant celle-ci |
* |
* @category php 5.2 |
* @package del |
* @subpackage scripts |
* @author Aurélien PERONNET <aurelien@tela-botanica.org> |
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org) |
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL |
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL |
* @version $Id$ |
*/ |
class AlerteMailADeterminer { |
private $parametres; |
private $conteneur; |
private $observations_concernees = array(); |
private $id_observation_commentees = array(); |
private $correspondance_id_obs_auteurs = array(); |
public function __construct($parametres, $conteneur) { |
$this->parametres = $parametres; |
$this->conteneur = $conteneur; |
} |
public function executer() { |
try { |
$observations = $this->chargerObsADeterminer(); |
if(!empty($observations)) { |
$this->formaterObservations($observations); |
$this->formaterListeObservationsEtEnvoyerMessage($this->observations_concernees); |
} |
} catch (Exception $e) { |
$this->traiterErreur($e->getMessage()); |
} |
} |
protected function chargerObsADeterminer() { |
//TODO: optimiser la requête si beaucoup de mails sont envoyés |
$requete = "SELECT * ". |
"FROM del_obs_image AS doi ". |
" INNER JOIN del_observation AS do ON doi.id_observation = do.id_observation ". |
" LEFT JOIN del_utilisateur AS du ON do.ce_utilisateur = du.id_utilisateur AND do.ce_utilisateur != 0 ". |
'WHERE (do.mots_cles_texte LIKE "%aDeterminer%" OR nom_sel IS NULL OR nom_ret IS NULL) '. |
' AND ('. |
' do.date_transmission > DATE_SUB(CURDATE(), INTERVAL 1 DAY) '. |
' OR do.date_creation > DATE_SUB(CURDATE(), INTERVAL 1 DAY) '. |
' OR do.date_modification > DATE_SUB(CURDATE(), INTERVAL 1 DAY) '. |
' ) '. |
"ORDER BY do.id_observation "; |
$obs = $this->conteneur->getBdd()->recupererTous($requete); |
return $obs; |
} |
protected function formaterObservations($liste_obs) { |
foreach ($liste_obs as $obs) { |
$id_obs = $obs['id_observation']; |
$auteur_obs = $this->formaterAuteur($obs); |
$obs['date'] = ($obs['date_observation'] != '0000-00-00 00:00:00') ? $this->formaterDateObs($obs['date_observation']) : '(date inconnue)'; |
$obs['lien'] = $this->obtenirLienFicheObs($obs['id_observation']); |
$obs['nom_propose'] = $this->formaterNomSci($obs); |
$this->observations_concernees[$id_obs] = $obs; |
} |
} |
protected function formaterListeObservationsEtEnvoyerMessage($liste_obs) { |
$message = $this->formaterMessagePourListeObs($liste_obs); |
$this->envoyerMessageListeDetermination($message); |
$utilisateurs = $this->obtenirDestinatairesMailsRecapitulatif(); |
foreach ($utilisateurs as $utilisateur) { |
$this->envoyerMessage($message, $utilisateur['id_utilisateur']); |
} |
} |
private function formaterMessagePourListeObs($liste_obs) { |
$donnees = array(); |
$donnees['liste_observations'] = $liste_obs; |
$this->squelette_dossier = dirname(__FILE__).DIRECTORY_SEPARATOR.'squelettes'.DIRECTORY_SEPARATOR; |
return $this->conteneur->traiterSquelettePhp($this->squelette_dossier.'aDeterminer.tpl.html', $donnees); |
} |
protected function formaterDateObs($date) { |
$time = strtotime($date); |
return date('d/m/Y', $time); |
} |
private function formaterAuteur($obs) { |
return $obs['prenom'].' '.$obs['nom']; |
} |
private function formaterNomSci($obs) { |
$nom = ''; |
if ($obs['nom_ret'] != '') { |
$nom = $obs['nom_ret']; |
} else if($obs['nom_sel'] != '') { |
$nom = $obs['nom_sel']; |
} |
return $nom; |
} |
private function obtenirLienFicheObs($id_obs) { |
return $this->conteneur->formaterUrl(Config::get('url_fiche_observation'), array('id' => $id_obs)); |
} |
private function obtenirDestinatairesMailsRecapitulatif() { |
$preference = $this->conteneur->getBdd()->proteger('%"mail_notification_toutes_obs":"1"%'); |
$requete = "SELECT id_utilisateur ". |
"FROM del_utilisateur_infos ". |
"WHERE preferences LIKE $preference "; |
$utilisateurs = $this->conteneur->getBdd()->recupererTous($requete); |
return $utilisateurs; |
} |
/** |
* Appelle le web service message de l'annuaire afin d'envoyer un mail à l'utilisateur dont l'identifiant est donné |
* en paramètre |
* Enter description here ... |
* @param string $message |
* @param int $id_destinataire |
*/ |
protected function envoyerMessage($message, $id_destinataire) { |
$url_requete = $this->conteneur->formaterUrl(Config::get('url_service_messagerie'), array('id' => $id_destinataire)); |
$post = array( |
'sujet' => 'Récapitulatif des dernières observations à déterminer', |
'message' => $message, |
'utilisateur_courriel' => Config::get('adresse_expediteur_recapitulatif'), |
'format' => 'html' |
); |
$ch = curl_init(); |
$options = array( |
CURLOPT_URL => $url_requete, |
CURLOPT_HEADER => false, |
CURLOPT_POST => 1, |
CURLOPT_POSTFIELDS => $post, |
CURLOPT_RETURNTRANSFER => true |
); |
curl_setopt_array($ch, $options); |
$envoi = curl_exec($ch); |
curl_close($ch); |
} |
/** La liste détermination n'étant pas un membre de tela botanica, |
* on lui envoie un message directement |
* @param string $message contenu du mail |
* @return boolean true ou false suivant le succès ou non de l'envoi |
*/ |
public function envoyerMessageListeDetermination($message) { |
$sujet = 'Récapitulatif des dernières observations à déterminer'; |
$message_html = $message; |
$message_texte = $this->supprimerHtml($message); |
$limite = "_----------=_parties_".md5(uniqid (rand())); |
// Définition d'un mail en texte simple et html |
$entetes = "X-Sender: <http://www.tela-botanica.org>\n". |
"X-Mailer: PHP\n". |
"X-auth-smtp-user: ".Config::get('adresse_expediteur_recapitulatif')." \n". |
"X-abuse-contact: ".Config::get('adresse_expediteur_recapitulatif')." \n". |
"Date: ".date("D, j M Y G:i:s O")."\n". |
'From: '.Config::get('adresse_expediteur_recapitulatif')."\n". |
// multipart/alternative signifie même contenu de la forme la plus simple à la plus complexe |
"Content-Type: multipart/alternative; boundary=\"".$limite."\";\n\n"; |
$contenu = "". |
// message en texte simple |
"--".$limite."\n". |
"Content-Type: text/plain; charset=\"".Config::get('encodage_appli')."\";\n". |
"Content-Transfer-Encoding: 7bit;\n\n". |
$message_texte."\n". |
// le message en html est préféré s'il est lisible |
"--".$limite."\n". |
"Content-Type: text/html;". |
"charset=\"".Config::get('encodage_appli')."\";\n". |
"Content-Transfer-Encoding: 7bit;\n\n". |
$message_html."\n". |
"--".$limite."--\n"; |
$envoieStatut = mail( |
Config::get('adresse_destinataire_recapitulatif_determination'), |
Config::get('titre_message_recapitulatif_obs'), |
$contenu, |
$entetes |
); |
return $envoieStatut; |
} |
/** Transforme automatiquement le message html en message txt. |
* |
* Réalise un stip_tags et avant ça un remplacement des liens sur mesure pour les mettre au format email txt. |
*/ |
private function supprimerHtml($messageHtml) { |
$html = $this->ajouterHrefDansBalise($messageHtml); |
$messageTxt = strip_tags($html); |
return $messageTxt; |
} |
/** |
* Extrait la valeur de l'attribut href des balises HTML de liens (a) et ajoute le lien entre |
* chevrons (<>) dans le contenu de la balise "a". |
*/ |
private function ajouterHrefDansBalise($html) { |
$dom = new DOMDocument; |
$dom->loadHTML($html); |
foreach ($dom->getElementsByTagName('a') as $node) { |
if ($node->hasAttribute( 'href' )) { |
$href = $node->getAttribute('href'); |
$node->nodeValue = $node->nodeValue." < $href >"; |
} |
} |
$html = $dom->saveHtml(); |
return $html; |
} |
} |
?> |
/branches/v1.2-lithium/scripts/modules/adeterminer/squelettes/aDeterminer.tpl.html |
---|
New file |
0,0 → 1,88 |
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> |
<html> |
<head> |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
<style type="text/css"> |
<!-- |
body { |
font-family:Arial,Helvetica,Verdana,sans-serif; |
font-size:14px; |
} |
h1 > a { |
color:#fd8c13; |
} |
h3 { |
font-size:14px; |
color:#454341 !important; |
margin:0px; |
} |
h3 > a { |
color:#454341; |
} |
h1 a, h3 a, h4 a { |
color:inherit; |
border-bottom:dotted 1px; |
} |
h3 a { |
border-color:#CCCCCC; |
} |
a { |
cursor:pointer; |
color:#598000; |
text-decoration:none; |
border-bottom:1px dotted #95ae5d; |
} |
a:hover { |
color:#FD8C13; |
border-bottom:1px dotted #95ae5d; |
} |
.lire-suite { |
padding-left:10px; |
background:url(http://www.tela-botanica.org/sites/commun/generique/images/graphisme/petit_carre.png) no-repeat 0px 4px; |
} |
#titre { |
background:none repeat scroll 0 0 #9AC343; |
opacity: 0.9; |
position: relative; |
text-align: center; |
width: 100%; |
} |
#titre h1 { |
background: url("http://www.tela-botanica.org/eflore-test/del/img/identiplante_mini.png") no-repeat scroll 150px 7px transparent; |
color: #777777; |
font-size: 2em; |
font-weight: bold; |
margin: 0 0 0; |
padding: 18px 60px; |
text-align: center; |
width: 100%; |
} |
.description_message { |
color: #333333; |
font-size: 1.1em; |
font-style: italic; |
padding-top: 10px; |
} |
--> |
</style> |
</head> |
<body> |
<div id="titre"> |
<h1>IdentiPlante - Récapitulatif des observations à déterminer </h1> |
</div> |
<div class="description_message"> |
Ce message récapitule l'ensemble des observations dont l'auteur a demandé confirmation ou détermination via |
l'outil <a href="http://www.tela-botanica.org/appli:del" >Identiplante</a> |
<br /><br /> |
</div> |
<?php foreach($liste_observations as $observation) : ?> |
<?php $nom_ret = (trim($observation['nom_propose']) != '') ? $observation['nom_propose'] : 'Espèce indéterminée'; ?> |
<h3> |
<a href="<?= $observation['lien']; ?>">Observation <?= $observation['id_observation'] ?> - <em><?= $nom_ret; ?></em> </a> |
observée le <?= $observation['date']; ?> à <?= $observation['zone_geo']; ?> |
</h3> |
<br /> |
<?php endforeach ; ?> |
</body> |
</html> |
/branches/v1.2-lithium/scripts/modules/commentaires/AlerteMailCommentaires.php |
---|
New file |
0,0 → 1,210 |
<?php |
// declare(encoding='UTF-8'); |
/** |
* Le script récupère tous les commentaires et déterminations fait ce jour et envoie un mail |
* aux auteurs des observations récapitulant celle-ci |
* |
* @category php 5.2 |
* @package del |
* @subpackage scripts |
* @author Aurélien PERONNET <aurelien@tela-botanica.org> |
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org) |
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL |
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL |
* @version $Id$ |
*/ |
class AlerteMailCommentaires { |
private $parametres; |
private $conteneur; |
private $observations_concernees = array(); |
private $id_observation_commentees = array(); |
private $correspondance_id_obs_auteurs = array(); |
public function __construct($parametres, $conteneur) { |
$this->parametres = $parametres; |
$this->conteneur = $conteneur; |
} |
public function executer() { |
try { |
$observations = $this->chargerObservationsCommentees(); |
if (!empty($observations)) { |
$this->formaterObservations($observations); |
$commentaires = $this->chargerCommentairesAjourdhui(); |
$this->formaterCommentaires($commentaires); |
$this->parcourirListeCommentairesEtEnvoyerMessage($this->observations_concernees); |
} |
} catch (Exception $e) { |
$this->traiterErreur($e->getMessage()); |
} |
} |
protected function chargerCommentairesAjourdhui() { |
$preferences = $this->conteneur->getBdd()->proteger('%"mail_notification_mes_obs":"0"%'); |
// TODO: optimiser la requête si beaucoup de mails sont envoyés |
$requete = "SELECT * ". |
"FROM del_commentaire AS dc ". |
"WHERE dc.ce_observation != '0' ". |
"AND dc.id_commentaire NOT IN ". |
"( ". |
" SELECT dc.id_commentaire ". |
" FROM del_commentaire AS dc, del_observation AS dob". |
" WHERE dc.nom_sel = dob.nom_sel ". |
" AND dc.ce_utilisateur = dob.ce_utilisateur ". |
" AND dc.ce_observation = dob.id_observation ". |
") ". |
"AND dc.ce_utilisateur NOT IN ". |
"( ". |
" SELECT id_utilisateur ". |
" FROM del_utilisateur_infos ". |
" WHERE preferences LIKE $preferences ". |
") ". |
"AND TO_DAYS(NOW()) - TO_DAYS(dc.date) <= 1 ". |
"ORDER BY dc.ce_observation"; |
$commentaires = $this->conteneur->getBdd()->recupererTous($requete); |
return $commentaires; |
} |
protected function chargerObservationsCommentees() { |
$preferences = $this->conteneur->getBdd()->proteger('%"mail_notification_mes_obs":"0"%'); |
//TODO: optimiser la requête si beaucoup de mails sont envoyés |
$requete = "SELECT * FROM del_observation AS do ". |
"WHERE do.id_observation IN ". |
"( ". |
"SELECT dc.ce_observation ". |
"FROM del_commentaire AS dc ". |
"WHERE dc.ce_observation != '0' ". |
"AND dc.id_commentaire NOT IN ". |
"( ". |
" SELECT dc.id_commentaire ". |
" FROM del_commentaire AS dc, del_observation AS dob". |
" WHERE dc.nom_sel = dob.nom_sel ". |
" AND dc.ce_utilisateur = dob.ce_utilisateur ". |
" AND dc.ce_observation = dob.id_observation ". |
") ". |
"AND dc.ce_utilisateur NOT IN ". |
"( ". |
" SELECT id_utilisateur ". |
" FROM del_utilisateur_infos ". |
" WHERE preferences LIKE $preferences ". |
") ". |
"AND TO_DAYS(NOW()) - TO_DAYS(dc.date) <= 1 ". |
") ". |
"ORDER BY do.ce_utilisateur, do.id_observation "; |
$observations = $this->conteneur->getBdd()->recupererTous($requete); |
return $observations; |
} |
protected function convertirCodeZoneGeoVersDepartement($code_zone_geo) { |
$code_departement = ''; |
if ($this->estUnCodeInseeDepartement($code_zone_geo)) { |
$code_departement = substr(ltrim($code_zone_geo, 'INSEE-C:'), 0, 2); |
} |
return $code_departement; |
} |
protected function estUnCodeInseeDepartement($code_a_tester) { |
return preg_match('/^INSEE-C:[0-9]{5}/',$code_a_tester); |
} |
protected function formaterObservations($liste_obs) { |
foreach ($liste_obs as $obs) { |
$id_obs = $obs['id_observation']; |
$auteur_obs = $obs['ce_utilisateur']; |
$obs['date'] = $this->formaterDateObs($obs['date_observation']); |
$obs['lieu'] = $this->formaterLieuObservation($obs); |
$obs['lien'] = $this->obtenirLienFicheObs($obs['id_observation']); |
$obs['commentaires'] = array(); |
$this->correspondance_id_obs_auteurs[$id_obs] = $auteur_obs; |
$this->observations_concernees[$auteur_obs][$id_obs] = $obs; |
} |
} |
protected function formaterLieuObservation($obs) { |
$lieux = array(); |
if (!empty($obs['zone_geo'])) { |
$zone_geo = $obs['zone_geo']; |
$zone_geo.= (!empty($obs['ce_zone_geo']) ? ' ('.$this->convertirCodeZoneGeoVersDepartement($obs['ce_zone_geo']).')' : ''); |
$lieux[] = $zone_geo; |
} |
if (!empty($obs['lieudit'])) { |
$lieux[] = $obs['lieudit']; |
} |
if (!empty($obs['station'])) { |
$lieux[] = $obs['station']; |
} |
return implode(" > ", $lieux); |
} |
protected function formaterCommentaires($liste_commentaires) { |
$liste_commentaires_formatee = array(); |
foreach ($liste_commentaires as $commentaire) { |
$id_obs = $commentaire['ce_observation']; |
if (isset($this->correspondance_id_obs_auteurs[$commentaire['ce_observation']])) { |
$auteur_obs = $this->correspondance_id_obs_auteurs[$commentaire['ce_observation']]; |
$commentaire['auteur'] = $this->formaterAuteur($commentaire); |
$this->observations_concernees[$auteur_obs][$id_obs]['commentaires'][] = $commentaire; |
} |
} |
} |
protected function parcourirListeCommentairesEtEnvoyerMessage($liste_obs_a_auteur) { |
foreach ($liste_obs_a_auteur as $auteur => $liste_obs) { |
$message = $this->formaterMessagePourAuteur($liste_obs,$auteur); |
$this->envoyerMessage($message, $auteur); |
} |
} |
private function formaterMessagePourAuteur($liste_obs, $auteur) { |
$donnees = array(); |
// copie de la première obs pour en obtenir le nom et prénom de l'auteur |
// (en deux fois pour éviter un warning en mode strict) |
$valeurs = array_values($liste_obs); |
$premiere_obs = array_shift($valeurs); |
$donnees['liste_observations'] = $liste_obs; |
$donnees['auteur'] = $auteur; |
$donnees['auteur_formate'] = ucfirst($premiere_obs['prenom_utilisateur']).' '.ucfirst($premiere_obs['nom_utilisateur']); |
$this->squelette_dossier = dirname(__FILE__).DIRECTORY_SEPARATOR.'squelettes'.DIRECTORY_SEPARATOR; |
return $this->conteneur->traiterSquelettePhp($this->squelette_dossier.'commentaires.tpl.html', $donnees); |
} |
protected function formaterDateObs($date) { |
$time = strtotime($date); |
return date('d/m/Y', $time); |
} |
private function formaterAuteur($commentaire) { |
return $commentaire['utilisateur_prenom'].' '.$commentaire['utilisateur_nom']; |
} |
private function obtenirLienFicheObs($id_obs) { |
return $this->conteneur->formaterUrl(Config::get('url_fiche_observation'), array('id' => $id_obs)); |
} |
protected function envoyerMessage($message, $auteur) { |
$url_requete = $this->conteneur->formaterUrl(Config::get('url_service_messagerie'), array('id' => $auteur)); |
$post = array( |
'sujet' => Config::get('titre_message_recapitulatif'), |
'message' => $message, |
'utilisateur_courriel' => Config::get('adresse_expediteur_recapitulatif'), |
'format' => 'html' |
); |
$ch = curl_init(); |
$options = array( |
CURLOPT_URL => $url_requete, |
CURLOPT_HEADER => false, |
CURLOPT_POST => 1, |
CURLOPT_POSTFIELDS => $post, |
CURLOPT_RETURNTRANSFER => true |
); |
curl_setopt_array($ch, $options); |
$envoi = curl_exec($ch); |
curl_close($ch); |
} |
} |
?> |
/branches/v1.2-lithium/scripts/modules/commentaires/Commentaires.php |
---|
New file |
0,0 → 1,41 |
<?php |
//declare(encoding='UTF-8'); |
/** |
* Exemple de lancement du script : : |
* /opt/lampp/bin/php cli.php coste -a chargerTous |
* |
* @category php 5.2 |
* @package eFlore/Scripts |
* @author Aurélien PERONNET <aurelien@tela-botanica.org> |
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org) |
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL |
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL |
* @version $Id$ |
*/ |
class Commentaires extends DelScript { |
public function executer() { |
try { |
$this->initialiserProjet('commentaires'); |
// Lancement de l'action demandée |
$cmd = $this->getParametre('a'); |
switch ($cmd) { |
case 'alerteMail' : |
$script = $this->chargerClasse('AlerteMailCommentaires'); |
$script->executer(); |
break; |
default : |
throw new Exception("Erreur : la commande '$cmd' n'existe pas!"); |
} |
} catch (Exception $e) { |
$this->traiterErreur($e->getMessage()); |
} |
} |
private function chargerClasse($classe) { |
require_once $classe.'.php'; |
return new $classe($this->parametres, $this); |
} |
} |
?> |
/branches/v1.2-lithium/scripts/modules/commentaires/squelettes/commentaires.tpl.html |
---|
New file |
0,0 → 1,122 |
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> |
<html> |
<head> |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
<style type="text/css"> |
<!-- |
body { |
font-family: Arial,Helvetica,Verdana,sans-serif; |
font-size: 14px; |
} |
h1 > a { |
color:#fd8c13; |
} |
.emphase { |
color: #777777; |
} |
h3 { |
font-size:14px; |
color:#454341 !important; |
margin:0px; |
} |
h3 > a { |
color:#454341; |
} |
h1 a, h3 a, h4 a { |
color: inherit; |
border-bottom: dotted 1px; |
} |
h3 a { |
border-color:#CCCCCC; |
} |
a { |
cursor: pointer; |
color:#598000; |
text-decoration:none; |
border-bottom:1px dotted #95ae5d; |
} |
a:hover { |
color:#FD8C13; |
border-bottom:1px dotted #95ae5d; |
} |
.lire-suite { |
padding-left:10px; |
background:url(http://www.tela-botanica.org/sites/commun/generique/images/graphisme/petit_carre.png) no-repeat 0px 4px; |
} |
#titre { |
background: none repeat scroll 0 0 #9AC343; |
opacity: 0.9; |
position: relative; |
text-align: center; |
width: 100%; |
} |
#titre h1 { |
background: url("http://www.tela-botanica.org/eflore-test/del/img/identiplante_mini.png") no-repeat scroll 150px 7px transparent; |
color: #777777; |
font-size: 2em; |
font-weight: bold; |
margin: 0 0 0; |
padding: 18px 60px; |
text-align: center; |
width: 100%; |
} |
.description_message { |
color: #333333; |
font-size: 1.1em; |
font-style: italic; |
padding-top: 10px; |
} |
--> |
</style> |
</head> |
<body> |
<div> |
<h1><span class="emphase">IdentiPlante</span> : un telabotaniste vous a aidé </h1> |
</div> |
<div class="description_message"> |
Bonjour <?= $auteur_formate ?>,<br /> |
<br /> |
</div> |
<?php foreach ($liste_observations as $observation) : ?> |
<span class="emphase">-></span> Un Telabotaniste vient de proposer une nouvelle détermination |
ou de faire un commentaire concernant votre observation de |
<?php $nom_ret = (trim($observation['nom_ret']) != '') ? $observation['nom_ret'] : 'Espèce indéterminée'; ?> |
<h3> |
<a href="<?= $observation['lien']; ?>"> |
Observation <?= $observation['id_observation'] ?> - <em><?= $nom_ret; ?></em> |
</a> |
du <?= $observation['date']; ?> à <?= $observation['lieu']; ?> |
</h3> |
<?php foreach ($observation['commentaires'] as $commentaire) : ?> |
<?php if ($commentaire['nom_sel'] == '') { ?> |
<div class="commentaire"> |
- <?= $commentaire['auteur'] ?> a écrit <em>"<?= $commentaire['texte'] ?>"</em> |
</div> |
<?php } else { ?> |
<div class="proposition"> |
- <?= $commentaire['auteur'] ?> a proposé <em><?= $commentaire['nom_sel'] ?></em><br /> |
<?php if ($commentaire['texte'] != '') : ?> |
et a écrit : <em>"<?= $commentaire['texte'] ?>"</em> |
<?php endif; ?> |
</div> |
<?php } ?> |
<?php endforeach ; ?> |
<br /> |
<?php endforeach ; ?> |
Bonne continuation sur nos outils botaniques !<br /> |
L'équipe de Tela Botanica<br /> |
<div>---------</div> |
<br/> |
<small> |
Ce message vous est envoyé par l'intermédiaire de l'application IdentiPlante du réseau Tela Botanica. |
<a href="http://www.tela-botanica.org/eflore">http://www.tela-botanica.org/appli:identiplante </a> |
</small> |
<br /> |
<small> |
Si vous ne souhaitez plus recevoir ce mail décochez la case dans vos préférences |
sur l'interface d'IdentiPlante. |
</small> |
</body> |
</html> |
/branches/v1.2-lithium/scripts/modules/maj/README-Index-maj1.txt |
---|
New file |
0,0 → 1,106 |
1) accélérer le SELECT + ORDER BY sur del_observation |
C'est un aspect important de la requête car il représente près de 33% du temps. |
En local, près de 50ms sur 1.5s. |
Solution: |
CREATE INDEX `date_transmission` ON `cel_obs` (`date_transmission` DESC) COMMENT "nécessaire à l'ORDER BY utilisé dans la liste d'observation de DEL"; |
Malheureusement l'INDEX ne semble pas utilisable par la view (http://bugs.mysql.com/bug.php?id=43341 ?): |
EXPLAIN SELECT SQL_CALC_FOUND_ROWS id_observation FROM del_observation ORDER BY date_transmission desc LIMIT 0, 12; |
# Using filesort (0.46s) |
Allons donc invoquer directement la table, cependant, encore une fois l'INDEX n'est pas utilisé |
EXPLAIN SELECT SQL_CALC_FOUND_ROWS id_observation FROM tb_cel.cel_obs ORDER BY date_transmission desc LIMIT 0, 12; |
# Using filesort (0.46s) |
Nous pouvons forcer l'index: |
EXPLAIN SELECT SQL_CALC_FOUND_ROWS id_observation FROM tb_cel.cel_obs FORCE INDEX FOR ORDER BY (`date_transmission`) ORDER BY date_transmission desc LIMIT 0, 12; |
# key=date_transmission (0.0s) |
parfait ! |
Cependant, à peine ajoutons-nous le WHERE transmission = 1 que nous remontons à 0.60 seconde, et MySQL nous déconseille même l'utilisation de l'INDEX |
qui, en effet, ralenti la requête (2.38s): |
EXPLAIN SELECT SQL_CALC_FOUND_ROWS id_observation FROM tb_cel.cel_obs FORCE INDEX FOR ORDER BY (`date_transmission`) WHERE transmission = 1 ORDER BY date_transmission desc LIMIT 0, 12; |
# Using where; Using filesort (0.60s) |
# Using where; (key=date_transmission) (2.38s) [avec FORCE INDEX] |
Dans tous les cas ci-dessus, "rows" indique "291265", et le type "ref", sauf en "FORCE INDEX", mais comme nous l'avons vu, cela sous-optimal. |
Le processing de transmission = 1 est très (très) couteux et c'est lui qui s'avère bloquant. |
Tentons l'ajout d'un INDEX sur transmission ,tout TINYINT soit-il: |
CREATE INDEX `transmission` ON `tb_cel`.`cel_obs` (`transmission`) COMMENT "nécessaire à CEL/DEL qui officie avec transmission = 1" |
ANALYZE TABLE tb_cel.cel_obs; |
EXPLAIN SELECT id_observation FROM tb_cel.cel_obs WHERE transmission = 1 ORDER BY date_transmission desc LIMIT 0, 12; |
# key = date_transmission, rows = 17, type = index (0.0s) |
parfait... |
et idem pour: |
EXPLAIN SELECT * FROM del_observation ORDER BY date_transmission desc LIMIT 0, 12; |
La vue prend donc bien en compte nos 2 INDEX. |
MAIS... ... |
EXPLAIN SELECT SQL_CALC_FOUND_ROWS id_observation FROM tb_cel.cel_obs WHERE transmission = 1 ORDER BY date_transmission desc LIMIT 0, 12; |
# Using where; Using filesort, key = date_transmission, rows = 291265, type = index (0.80s) |
... à peine retrouvons-nous SQL_CALC_FOUND_ROWS() que les perf' sont à nouveau déplorable puis MySQL itère sur les 300k lignes à transmission=1 |
Cependant: |
EXPLAIN SELECT count(1) FROM del_observation; |
# key = transmission, 291265 = 17, type = ref, MAIS... "Using index" (0.10s) |
Ainsi pour une requête initiale: |
SELECT SQL_CALC_FOUND_ROWS dob.id_observation, nom_sel, famille, ce_zone_geo, zone_geo, lieudit, station, milieu, date_observation, dob.mots_cles_texte, date_transmission, dob.ce_utilisateur, prenom, nom, courriel, dob.prenom_utilisateur, dob.nom_utilisateur, dob.courriel_utilisateur, dob.commentaire as dob_commentaire, dob.nt, dob.nom_sel_nn FROM del_observation AS dob LEFT JOIN del_utilisateur AS du ON dob.ce_utilisateur = du.id_utilisateur ORDER BY date_transmission desc LIMIT 0, 12 |
de 1.52s, la suppression SQL_CALC_FOUND_ROWS() nous amène à 0.00s + 0.10s de count() additionnel. |
Sans INDEX sur transmission, la requête est de même rapidité, mais le count(1), trois fois plus long (0.30s) |
Sans INDEX sur date_transmission, la requête est 4 fois plus longue (0.40s) et le count(1) identique (0.35s) |
Bref, les 2 INDEX sont intéressant même si l'idéal est de rendre optionnel l'affichage du total des résultats |
2) accélérer le GROUP en requête complexe: |
SELECT SQL_CALC_FOUND_ROWS dob.id_observation, nom_sel, famille, ce_zone_geo, zone_geo, lieudit, station, milieu, date_observation, dob.mots_cles_texte, date_transmission, di.id_image, dob.ce_utilisateur, prenom, nom, courriel, dob.prenom_utilisateur, dob.nom_utilisateur, dob.courriel_utilisateur, nom_original, dob.commentaire AS dob_commentaire, dob.nt, dob.nom_sel_nn FROM del_observation AS dob LEFT JOIN del_utilisateur AS du ON du.id_utilisateur = dob.ce_utilisateur LEFT JOIN del_obs_image AS doi ON doi.id_observation = dob.id_observation LEFT JOIN del_image AS di ON di.id_image = doi.id_image WHERE 1 GROUP BY doi.id_observation ORDER BY date_transmission desc LIMIT 0, 12 |
EXPLAIN SELECT dob.id_observation, di.id_image FROM cel_obs AS dob, cel_obs_images AS doi, cel_images AS di WHERE (transmission = 1 AND doi.id_observation = dob.id_observation AND di.id_image = doi.id_image) GROUP BY dob.id_observation ORDER BY date_transmission desc LIMIT 0, 12; |
+------+-------------+-------+--------+---------------------------+---------+---------+---------------------------+-------+----------------------------------------------+ |
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | |
+------+-------------+-------+--------+---------------------------+---------+---------+---------------------------+-------+----------------------------------------------+ |
| 1 | SIMPLE | doi | index | PRIMARY,observation,image | PRIMARY | 16 | NULL | 10314 | Using index; Using temporary; Using filesort | |
| 1 | SIMPLE | di | eq_ref | PRIMARY,id_image | PRIMARY | 8 | tb_cel.doi.id_image | 1 | Using index | |
| 1 | SIMPLE | dob | eq_ref | PRIMARY,transmission | PRIMARY | 8 | tb_cel.doi.id_observation | 1 | Using where | |
+------+-------------+-------+--------+---------------------------+---------+---------+---------------------------+-------+----------------------------------------------+ |
# 0.04s |
EXPLAIN SELECT dob.id_observation, di.id_image FROM del_observation AS dob, del_obs_image AS doi, del_image AS di WHERE (doi.id_observation = dob.id_observation AND di.id_image = doi.id_image) GROUP BY dob.id_observation ORDER BY date_transmission desc LIMIT 0, 12; |
+------+-------------+-------+--------+---------------------------+---------+---------+--------------------------+-------+----------------------------------------------+ |
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | |
+------+-------------+-------+--------+---------------------------+---------+---------+--------------------------+-------+----------------------------------------------+ |
| 1 | SIMPLE | oi | index | PRIMARY,observation,image | PRIMARY | 16 | NULL | 10314 | Using index; Using temporary; Using filesort | |
| 1 | SIMPLE | oi | ref | PRIMARY,observation,image | image | 8 | tb_cel.oi.id_image | 1 | | |
| 1 | SIMPLE | i | eq_ref | PRIMARY,id_image | PRIMARY | 8 | tb_cel.oi.id_image | 1 | Using index | |
| 1 | SIMPLE | o | eq_ref | PRIMARY,transmission | PRIMARY | 8 | tb_cel.oi.id_observation | 1 | Using where | |
| 1 | SIMPLE | o | eq_ref | PRIMARY,transmission | PRIMARY | 8 | tb_cel.oi.id_observation | 1 | Using where | |
| 1 | SIMPLE | o | eq_ref | PRIMARY,transmission | PRIMARY | 8 | tb_cel.oi.id_observation | 1 | Using where | |
+------+-------------+-------+--------+---------------------------+---------+---------+--------------------------+-------+----------------------------------------------+ |
# 1.67s |
En fait, del_image se définit comme suit: |
select `i`.`id_image` AS `id_image`,if((char_length(`i`.`ce_utilisateur`) <> 32),cast(`i`.`ce_utilisateur` as unsigned),0) AS `ce_utilisateur`,`i`.`prenom_utilisateur` AS `prenom_utilisateur`,`i`.`nom_utilisateur` AS `nom_utilisateur`,`i`.`courriel_utilisateur` AS `courriel_utilisateur`,`i`.`hauteur` AS `hauteur`,`i`.`largeur` AS `largeur`,`i`.`date_prise_de_vue` AS `date_prise_de_vue`,`i`.`mots_cles_texte` AS `mots_cles_texte`,`i`.`commentaire` AS `commentaire`,`i`.`nom_original` AS `nom_original`,`i`.`date_modification` AS `date_modification`,`i`.`date_creation` AS `date_creation`,`i`.`publiable_eflore` AS `publiable_eflore` from ((`tb_cel`.`cel_images` `i` join `tb_cel`.`cel_obs_images` `oi` on((`i`.`id_image` = `oi`.`id_image`))) join `tb_cel`.`cel_obs` `o` on((`oi`.`id_observation` = `o`.`id_observation`))) where (`o`.`transmission` = '1') |
soit déjà une double-jointure. |
EXPLAIN SELECT id_observation, id_image FROM del_image AS di GROUP BY id_observation ORDER BY date_transmission desc LIMIT 0, 12; |
+------+-------------+-------+--------+---------------------------+---------+---------+--------------------------+-------+----------------------------------------------+ |
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | |
+------+-------------+-------+--------+---------------------------+---------+---------+--------------------------+-------+----------------------------------------------+ |
| 1 | SIMPLE | oi | index | PRIMARY,observation,image | PRIMARY | 16 | NULL | 10314 | Using index; Using temporary; Using filesort | |
| 1 | SIMPLE | i | eq_ref | PRIMARY,id_image | PRIMARY | 8 | tb_cel.oi.id_image | 1 | Using index | |
| 1 | SIMPLE | o | eq_ref | PRIMARY,transmission | PRIMARY | 8 | tb_cel.oi.id_observation | 1 | Using where | |
+------+-------------+-------+--------+---------------------------+---------+---------+--------------------------+-------+----------------------------------------------+ |
# 0.04s |
/branches/v1.2-lithium/scripts/modules/maj/201309-index-views.sql |
---|
New file |
0,0 → 1,74 |
-- r1368/r1418: ajout de nom_referentiel à del_observations (attention, appelée "del_images" dans mysql-workbench) |
DROP VIEW IF EXISTS `BASEDEL`.`del_image` ; |
DROP TABLE IF EXISTS `BASEDEL`.`del_image`; |
CREATE OR REPLACE VIEW `BASEDEL`.`del_image` AS |
SELECT i.id_image, if((char_length(i.ce_utilisateur) <> 32),cast(i.ce_utilisateur as unsigned),0) AS ce_utilisateur, i.prenom_utilisateur, i.nom_utilisateur, i.courriel_utilisateur, i.hauteur, i.largeur, i.date_prise_de_vue, i.mots_cles_texte, |
i.commentaire, i.nom_original, i.date_modification, i.date_creation, i.publiable_eflore, o.nom_referentiel |
FROM `BASECEL`.`cel_images` AS i |
INNER JOIN `BASECEL`.`cel_obs_images` AS oi ON (i.id_image = oi.id_image) |
INNER JOIN `BASECEL`.`cel_obs` AS o ON (oi.id_observation = o.id_observation) |
WHERE o.transmission = '1' ; |
-- r1368/r1418: ajout de nom_referentiel à del_observations |
DROP VIEW IF EXISTS `BASEDEL`.`del_observations` ; |
DROP TABLE IF EXISTS `BASEDEL`.`del_observations`; |
CREATE OR REPLACE VIEW `BASEDEL`.`del_observation` AS |
SELECT id_observation, if((char_length(o.ce_utilisateur) <> 32),cast(o.ce_utilisateur as unsigned),0) AS ce_utilisateur, prenom_utilisateur, nom_utilisateur, courriel_utilisateur, nom_sel, nom_sel_nn, nom_ret, nom_ret_nn, nt, famille, |
ce_zone_geo, zone_geo, lieudit, station, milieu, nom_referentiel, |
date_observation, mots_cles_texte, commentaire, |
date_creation, date_modification, date_transmission |
FROM `BASECEL`.`cel_obs` AS o |
WHERE transmission = '1' ; |
-- r1376: ajout de la vue v_del_image (version modifiée de del_image |
DROP VIEW IF EXISTS `BASEDEL`.`v_del_image` ; |
DROP TABLE IF EXISTS `BASEDEL`.`v_del_image`; |
CREATE OR REPLACE VIEW `BASEDEL`.`v_del_image` AS |
SELECT o.*, i.id_image, i.ordre AS i_ordre, |
i.ce_utilisateur AS i_ce_utilisateur, i.prenom_utilisateur AS i_prenom_utilisateur, i.nom_utilisateur AS i_nom_utilisateur, i.courriel_utilisateur AS i_courriel_utilisateur, |
i.mots_cles_texte AS i_mots_cles_texte, hauteur, largeur, date_prise_de_vue, i.commentaire AS i_commentaire, nom_original, publiable_eflore |
FROM `BASECEL`.cel_obs AS o |
INNER JOIN `BASECEL`.cel_obs_images oi ON oi.id_observation = o.id_observation |
INNER JOIN `BASECEL`.cel_images i ON i.id_image = oi.id_image |
WHERE o.transmission = '1'; |
-- r1853 de CEL: ajout des INDEX (note: pas de commentaires sur les INDEX < MySQL 5.5) |
CREATE INDEX `date_transmission` ON `BASECEL`.`cel_obs` (`date_transmission` DESC) /* COMMENT "nécessaire à l'ORDER BY utilisé dans la liste d'observation de DEL" */ ; |
CREATE INDEX `transmission` ON `BASECEL`.`cel_obs` (`transmission`) /* COMMENT "nécessaire à CEL/DEL qui officie avec transmission = 1" */ ; |
-- cleanup |
DROP INDEX `tag_normalise_2` ON `BASEDEL`.`del_image_tag` ; |
DROP INDEX `ce_image_2` ON `BASEDEL`.`del_image_tag` ; |
DROP TABLE IF EXISTS `BASEDEL`.`del_image_stat`; |
CREATE TABLE IF NOT EXISTS `BASEDEL`.`del_image_stat` ( |
`ce_image` BIGINT(20) NOT NULL COMMENT 'id_image (tb_cel.cel_images)', |
`ce_protocole` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'un id de protocole', |
`moyenne` FLOAT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'moyenne des votes pour une image et un protocole donné', |
`nb_votes` SMALLINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'nombre de votes pour une image et un protocole donné', |
`nb_tags` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'nombre de tags pictoflora pour une image donnée', |
PRIMARY KEY (`ce_image`, `ce_protocole`), |
KEY `ce_image` (`ce_image`), |
KEY `ce_protocole` (`ce_protocole`, `moyenne` DESC), |
KEY `nb_votes` (`nb_votes` DESC), |
KEY `nb_tags` (`nb_tags` DESC) |
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'table de stockage des statistiques utilisées pour les tri de PictoFlora'; |
INSERT INTO `BASEDEL`.`del_image_stat` ( |
SELECT id_image, divo.ce_protocole, divo.moyenne, divo.nb_votes, dit.ctags |
FROM `BASECEL`.`cel_images` ci |
LEFT JOIN |
( SELECT ce_image, ce_protocole, AVG(valeur) AS moyenne, COUNT(valeur) AS nb_votes |
FROM del_image_vote GROUP BY ce_image, ce_protocole ) AS divo |
ON ci.id_image = divo.ce_image |
LEFT JOIN |
( SELECT ce_image, COUNT(id_tag) as ctags FROM del_image_tag |
GROUP BY ce_image ) AS dit |
ON ci.id_image = dit.ce_image ); |
ALTER TABLE `BASEDEL`.`del_image_protocole` ADD `mots_cles` VARCHAR(600) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; |
/branches/v1.2-lithium/scripts/modules/maj/201310-propret.sql |
---|
New file |
0,0 → 1,103 |
DELIMITER | |
-- initialisation (structure) |
DROP PROCEDURE IF EXISTS update_struct; |
CREATE PROCEDURE update_struct() |
BEGIN |
-- ajout de la colonne "proposition_retenue" |
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S21' SELECT "colonne proposition_retenue déjà existante"; /* = Duplicate column name */ |
ALTER TABLE `BASEDEL`.`del_commentaire` ADD `proposition_retenue` INT(1) NOT NULL DEFAULT 0 COMMENT "La proposition \"validée\" une fois que l'auteur à validé et que le nom_sel de cel_obs a été modifié à partir du nom_sel de del_commentaire."; -- ' |
END; |
| |
-- petite functon bonus |
DROP PROCEDURE IF EXISTS getCommentaires; |
CREATE PROCEDURE getCommentaires(IN _s1 INT(20)) |
BEGIN |
SELECT id_commentaire, ce_observation, ce_proposition, texte, nom_sel, nom_sel_nn, proposition_retenue FROM `BASEDEL`.`del_commentaire` WHERE ce_observation = _s1; |
END; |
| |
-- procédure créant des tables temporaires permettant de faciliter le processus de MAJ |
DROP PROCEDURE IF EXISTS rebuild_retenues; |
CREATE PROCEDURE rebuild_retenues() |
BEGIN |
DROP TEMPORARY TABLE IF EXISTS _temp_having_retenue, _temp_multi_prop, _temp_mono_prop; |
-- les observations déjà retenues (pour pouvoir être exclues des processus postérieurs) |
CREATE TEMPORARY TABLE _temp_having_retenue ENGINE=MEMORY AS ( \ |
SELECT ce_observation FROM `BASEDEL`.`del_commentaire` co WHERE proposition_retenue = 1); |
-- observations ayant plusieurs propositions (= commentaire dont nom_sel IS NOT NULL) |
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_multi_prop ENGINE=MEMORY AS ( \ |
SELECT ce_observation FROM `BASEDEL`.`del_commentaire` co \ |
WHERE nom_sel IS NOT NULL \ |
AND proposition_retenue = 0 /* == ce_observation NOT IN (SELECT ce_proposition FROM _temp_having_retenue) */ \ |
GROUP BY ce_observation HAVING COUNT(id_commentaire) > 1); -- 1053 |
-- observations ayant une seule proposition (= commentaire dont nom_sel IS NOT NULL) |
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_mono_prop ENGINE=MEMORY AS ( \ |
SELECT ce_observation FROM `BASEDEL`.`del_commentaire` \ |
WHERE nom_sel IS NOT NULL \ |
AND proposition_retenue = 0 /* == ce_observation NOT IN (SELECT ce_proposition FROM _temp_having_retenue) */ \ |
GROUP BY ce_observation HAVING COUNT(id_commentaire) = 1); |
END; |
| |
DELIMITER ; |
CALL update_struct(); |
-- table temporaire de tous les comptages (somme des votes pour chaque proposition de del_commentaire): |
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_stats ENGINE=MEMORY AS ( SELECT ce_proposition, SUM(valeur) AS val FROM `BASEDEL`.`del_commentaire_vote` GROUP BY ce_proposition); -- 5912 |
-- propositions ayant eu au moins 1 vote positif non-anonyme |
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_authvotepos ENGINE=MEMORY AS (SELECT DISTINCT ce_proposition FROM `BASEDEL`.`del_commentaire_vote` WHERE valeur = 1 AND LENGTH(ce_utilisateur) != 32 ORDER BY ce_proposition); -- 3794 |
CALL rebuild_retenues(); -- initialisation |
-- core |
-- SELECT ce_observation, id_commentaire FROM `BASEDEL`.`del_commentaire` co INNER JOIN _temp_multi_prop mp USING (ce_observation) INNER JOIN `BASECEL`.`cel_obs` ce ON mp.ce_observation = id_observation AND (co.nom_sel = ce.nom_sel AND co.nom_sel_nn = ce.nom_sel_nn) WHERE co.nom_sel_nn != 0 AND co.proposition_retenue = 0; -- 470, dont 464 ce_observation unique |
-- SELECT ce_observation, id_commentaire FROM `BASEDEL`.`del_commentaire` co INNER JOIN _temp_multi_prop mp USING (ce_observation) INNER JOIN `BASECEL`.`cel_obs` ce ON mp.ce_observation = id_observation AND (co.nom_sel = ce.nom_sel OR co.nom_sel_nn = ce.nom_sel_nn) WHERE co.nom_sel_nn != 0 AND co.proposition_retenue = 0; -- 520, dont 493 ce_observation unique |
-- Étant donné les doublons nous pouvons commencer ou finir par filtrer sur les stats de vote |
-- Comme il est plus simple de finir par ça, commençons correctement par les nom_sel comme ci-dessus avec ceux sans ambiguité: |
-- C'est à dire observations dotées de multiples proposition dont une et une seule match exactement le nom-sel du carnet en ligne |
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_updatable1 ENGINE=MEMORY AS ( \ |
SELECT /* ce_observation, */ id_commentaire FROM `BASEDEL`.`del_commentaire` co \ |
INNER JOIN _temp_multi_prop mp USING (ce_observation) /* faisant partie des observations ayant plusieurs propositions */ \ |
INNER JOIN `BASECEL`.`cel_obs` ce ON mp.ce_observation = id_observation AND (co.nom_sel = ce.nom_sel AND co.nom_sel_nn = ce.nom_sel_nn) \ |
WHERE co.nom_sel_nn != 0 \ |
GROUP BY co.ce_observation HAVING COUNT(co.id_commentaire) = 1 /* observations n'ayant pas d'ambiguité sur le commentaire correspondant au nom du carnet en ligne */ ); -- 335 |
UPDATE `BASEDEL`.`del_commentaire` co SET co.proposition_retenue = 1 WHERE co.id_commentaire IN (SELECT id_commentaire FROM _temp_updatable1); |
SELECT ROW_COUNT() AS "nb MAJ par match sur nom_sel sans ambiguité"; |
CALL rebuild_retenues(); |
-- Pour les observations ayant plusieurs nom-sel communs au carnet en ligne, il s'agit de ne sélectionner que celle |
-- ayant le plus de vote (idéalement les propositions doublonnes ne devraient pas exister) |
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_updatable2 ENGINE=MEMORY AS ( \ |
SELECT co.ce_observation, id_commentaire, i1.val FROM `BASEDEL`.`del_commentaire` co \ |
INNER JOIN _temp_multi_prop mp USING (ce_observation) /* uniquement les multi-propositions, inclue le check sur _temp_having_retenue */ \ |
INNER JOIN (SELECT ce_proposition, val FROM _temp_stats) AS i1 ON i1.ce_proposition = co.id_commentaire /* et ayant des votes */ \ |
INNER JOIN `BASECEL`.`cel_obs` ce ON co.ce_observation = ce.id_observation AND (co.nom_sel = ce.nom_sel AND co.nom_sel_nn = ce.nom_sel_nn) /* et un nom_sel acceptable */ \ |
WHERE co.nom_sel_nn != 0 \ |
GROUP BY ce_observation HAVING i1.val = MAX(i1.val) /* parmis les doublonnes, prendre la proposition ayant le plus de SUM(votes) */ \ |
ORDER BY ce_observation ); -- 249 (sans le processus précédent, moins autrement s'il l'on exclue les observation déjà "validée") |
UPDATE `BASEDEL`.`del_commentaire` co SET co.proposition_retenue = 1 WHERE co.id_commentaire IN (SELECT id_commentaire FROM _temp_updatable2); |
SELECT ROW_COUNT() AS "nb MAJ par nombre de vote"; |
CALL rebuild_retenues(); |
-- Enfin, les observation n'ayant qu'une seule proposition sont passées comme retenues si |
-- elles ont été notée positivement au moins une fois par un utilisateur authentifié |
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_updatable3 ENGINE=MEMORY AS ( \ |
SELECT co.id_commentaire FROM `BASEDEL`.`del_commentaire` co \ |
INNER JOIN _temp_mono_prop AS i1 USING (ce_observation) /* l'inverse de _temp_multi_prop, inclue le check sur _temp_having_retenue */ \ |
INNER JOIN _temp_authvotepos ta ON co.id_commentaire = ta.ce_proposition /* et uniquement si faisant partie des commentaires doté d'un vote positif non-anonyme */ \ |
INNER JOIN `BASECEL`.`cel_obs` ce ON co.ce_observation = ce.id_observation AND (co.nom_sel = ce.nom_sel AND co.nom_sel_nn = ce.nom_sel_nn) \ |
WHERE co.nom_sel_nn != 0); -- 2216 sans test sur cel_obs, 2193 avec |
UPDATE `BASEDEL`.`del_commentaire` co SET co.proposition_retenue = 1 WHERE co.id_commentaire IN (SELECT id_commentaire FROM _temp_updatable3); |
SELECT ROW_COUNT() AS "nb MAJ ayant 1 seule proposition mais un vote non-anonyme positif"; |
CALL rebuild_retenues(); |
/branches/v1.2-lithium/scripts/modules/maj/Makefile |
---|
New file |
0,0 → 1,36 |
celdb = tb_cel_test |
deldb = tb_del_test |
# helper |
do_subst = sed -e "1i--\n-- fichier d'origine: \"${1}\"\n" \ |
-e 's/`BASECEL`/`$(celdb)`/g' \ |
-e 's/`BASEDEL`/`$(deldb)`/g' \ |
$(1) |
# default target |
help: |
@echo "make o_maj1 [celdb=<$(celdb)>] [deldb=<$(deldb)>]" |
clean: |
rm -f *.comp.sql |
# maj1 contient: 201309-index-views.sql |
o_maj1: fichiers_generes = $(addsuffix .comp.sql,$(filter-out clean,$?)) |
o_maj1: clean o_201309-index-views |
cat $(fichiers_generes) > maj1.comp.sql |
# maj1 contient: 201310-propret.sql |
o_maj2: fichiers_generes = $(addsuffix .comp.sql,$(filter-out clean,$?)) |
o_maj2: clean o_201310-propret |
cat $(fichiers_generes) > maj2.comp.sql |
# SHOW INDEX FROM cel_obs WHERE Key_name = 'transmission'; |
# SHOW INDEX FROM cel_obs WHERE Key_name = 'date_transmission'; |
o_201309-index-views: |
$(call do_subst,201309-index-views.sql) > $@.comp.sql |
# SHOW COLUMNS FROM del_commentaire LIKE 'proposition_retenu'; |
o_201310-propret: |
$(call do_subst,201310-propret.sql) > $@.comp.sql |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/branches/v1.2-lithium/scripts/modules/maj |
---|
New file |
Property changes: |
Added: svn:ignore |
+o_*.comp.sql |
/branches/v1.2-lithium/scripts/configurations/config.defaut.ini |
---|
New file |
0,0 → 1,75 |
; Encodage : UTF-8 |
; +------------------------------------------------------------------------------------------------------+ |
; URLs |
; Le séparateur utilisé par le framework lorsqu'il génère des URL pour séparer les arguments. |
; Pour remettre les valeurs par défaut, utitliser : "php:ini_get('arg_separator.output')" |
url_arg_separateur_sortie = "&" |
; +------------------------------------------------------------------------------------------------------+ |
; Info sur l'application |
info.nom = Scripts d'identiplante |
; Abréviation de l'application |
info.abr = del-services |
; Version du Framework nécessaire au fonctionnement de cette application |
info.framework.version = 0.3 |
;Encodage de l'application |
encodage_appli = "UTF-8" |
; +------------------------------------------------------------------------------------------------------+ |
; Débogage |
; Indique si oui ou non on veut afficher le débogage. |
debogage = true |
; Indique sous quelle forme les méssages de débogage doivent s'afficher : |
; - "php:Debug::MODE_ECHO" : le message est affiché en utilisant echo |
; - "php:Debug::MODE_NOTICE" : le message est affiché en utilisant une erreur de type notice |
; - "php:Debug::MODE_ENTETE_HTTP" : les messages sont envoyés dans un entête HTTP "X_REST_DEBOGAGE". |
; - "Autre valeur" : les messages sont formatés puis retournés par la méthode de débogage utilisée. |
debogage_mode = "php:Debug::MODE_ECHO" |
; Indique si oui ou non on veut lancer le chronométrage |
chronometrage = false |
; +------------------------------------------------------------------------------------------------------+ |
; Paramètrage de la base de données. |
; Abstraction de la base de données. |
bdd_abstraction = pdo |
; Protocole de la base de données. |
bdd_protocole = mysql |
; Nom du serveur de bases de données. |
bdd_serveur = localhost |
; Nom de l'utilisateur de la base de données. |
bdd_utilisateur = "" |
; Mot de passe de l'utilisateur de la base de données. |
bdd_mot_de_passe = "" |
; Nom de la base de données principale. |
bdd_nom = "del" |
; Encodage de la base de données principale au format base de données (ex. pour l'utf-8 ne pas mettre le tiret!). |
bdd_encodage = "utf8" |
; +------------------------------------------------------------------------------------------------------+ |
; Standard utilisé pour les codes de langue |
langue_source = "ISO-639-1"; |
; Standard utilisé pour les codes de zones géographiques |
zone_geo_source = "ISO-3166-1"; |
; +------------------------------------------------------------------------------------------------------+ |
; Url de la fiche de l'observation |
url_fiche_observation = "http://www.tela-botanica.org/appli:identiplante#obs~{id}"; |
; +------------------------------------------------------------------------------------------------------+ |
; Url du service de messagerie de l'annuaire |
url_service_messagerie = "http://www.tela-botanica.org/client/annuaire_nouveau/actuelle/jrest/Utilisateur/{id}/message"; |
; Titre du message de récapitulatif d'identiplante |
titre_message_recapitulatif = "Récapitulatif identiplante"; |
; Adresse de l'expéditeur des messages récapitulatifs |
adresse_expediteur_recapitulatif = "identiplante-determination@tela-botanica.org" |
; Adresse destinataire des messages récapitulatifs de détermination |
; adresse_destinataire_recapitulatif_determination = "determination_plantes@tela-botanica.org" |
adresse_destinataire_recapitulatif_determination = "test@tela-botanica.org" |
; titre du message de récapitulatif des obs à déterminer |
titre_message_recapitulatif_obs = "Récapitulatif des dernières observations à déterminer" |
; +------------------------------------------------------------------------------------------------------+ |
; Espaces de noms |
cheminDelCommun = '../commun' |
/branches/v1.2-lithium/scripts/configurations |
---|
New file |
Property changes: |
Added: svn:ignore |
+config.ini |
/branches/v1.2-lithium/scripts/framework.defaut.php |
---|
New file |
0,0 → 1,6 |
<?php |
// Inclusion du Framework |
// Renomer ce fichier en "framework.php" |
// Indiquer ci-dessous le chemin absolu vers le fichier autoload.inc.php de la bonne version du Framework |
require_once '/home/www/commun/framework/0.3/Framework.php'; |
?> |
/branches/v1.2-lithium/scripts/bibliotheque/DelScript.php |
---|
New file |
0,0 → 1,117 |
<?php |
/** |
* Classe mère des scripts del |
* |
* @category php 5.2 |
* @package del |
* @subpackage scripts |
* @author Aurélien PERONNET <aurelien@tela-botanica.org> |
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org) |
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL |
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL |
* @version $Id$ |
*/ |
abstract class DelScript extends Script { |
private $Bdd = null; |
private $projetNom = null; |
public function getProjetNom() { |
return $this->projetNom; |
} |
protected function initialiserProjet($projetNom) { |
$this->projetNom = $projetNom; |
} |
//+------------------------------------------------------------------------------------------------------+ |
// Méthodes d'url |
/** |
* Formate une url à partir d'un template contenant des paramètres à remplacer sous la forme {monParametre}. |
* Le tableau associatif de paramètres doit contenir en clé le paramêtre (monParametre) sans les accolades, |
* la valeur correspondante sera la valeur de remplacement. |
* Par défaut, les parametres suivant sont pris en compte par cette méthode : |
* - {projet} : le code du référentiel courrant ou définit dans le constructeur de l'objet métier. |
* |
* @param String $tpl le squelette d'url à formater. |
* @param Array $parametres le tableau de parametres (sans accolades pour les clés).*/ |
public function formaterUrl($tpl, Array $parametres) { |
foreach ($parametres as $key=> $value) { |
$tpl = str_replace('{'.$key.'}', rawurlencode($value), $tpl); |
} |
return $tpl; |
} |
//+------------------------------------------------------------------------------------------------------+ |
// Méthodes d'accès aux objets du Framework |
/** |
* Méthode de connection à la base de données sur demande. |
* Tous les scripts n'ont pas besoin de s'y connecter. |
*/ |
public function getBdd() { |
if (! isset($this->Bdd)) { |
$this->Bdd = new Bdd(); |
} |
return $this->Bdd; |
} |
//+----------------------------------------------------------------------------------------------------------------+ |
// GESTION DES SQUELETTES PHP |
/** |
* Méthode prenant en paramètre un chemin de fichier squelette et un tableau associatif de données, |
* en extrait les variables, charge le squelette et retourne le résultat des deux combinés. |
* |
* @param String $fichier le chemin du fichier du squelette |
* @param Array $donnees un tableau associatif contenant les variables a injecter dans le squelette. |
* |
* @return boolean false si le squelette n'existe pas, sinon la chaine résultat. |
*/ |
public function traiterSquelettePhp($fichier, Array $donnees = array()) { |
$sortie = false; |
if (file_exists($fichier)) { |
// Extraction des variables du tableau de données |
extract($donnees); |
// Démarage de la bufferisation de sortie |
ob_start(); |
// Si les tags courts sont activés |
if ((bool) @ini_get('short_open_tag') === true) { |
// Simple inclusion du squelette |
include $fichier; |
} else { |
// Sinon, remplacement des tags courts par la syntaxe classique avec echo |
$html_et_code_php = self::traiterTagsCourts($fichier); |
// Pour évaluer du php mélangé dans du html il est nécessaire de fermer la balise php ouverte par eval |
$html_et_code_php = '?>'.$html_et_code_php; |
// Interprétation du html et du php dans le buffer |
echo eval($html_et_code_php); |
} |
// Récupèration du contenu du buffer |
$sortie = ob_get_contents(); |
// Suppression du buffer |
@ob_end_clean(); |
} else { |
$msg = "Le fichier du squelette '$fichier' n'existe pas."; |
trigger_error($msg, E_USER_WARNING); |
} |
// Retourne le contenu |
return $sortie; |
} |
/** |
* Fonction chargeant le contenu du squelette et remplaçant les tags court php (<?= ...) par un tag long avec echo. |
* |
* @param String $chemin_squelette le chemin du fichier du squelette |
* |
* @return string le contenu du fichier du squelette php avec les tags courts remplacés. |
*/ |
private function traiterTagsCourts($chemin_squelette) { |
$contenu = file_get_contents($chemin_squelette); |
// Remplacement de tags courts par un tag long avec echo |
$contenu = str_replace('<?=', '<?php echo ', $contenu); |
// Ajout systématique d'un point virgule avant la fermeture php |
$contenu = preg_replace("/;*\s*\?>/", "; ?>", $contenu); |
return $contenu; |
} |
} |
?> |
/branches/v1.2-lithium/scripts/cli.php |
---|
New file |
0,0 → 1,60 |
<?php |
// Encodage : UTF-8 |
// +-------------------------------------------------------------------------------------------------------------------+ |
/** |
* Initialise le chargement et l'exécution des scripts |
* |
* Lancer ce fichier en ligne de commande avec : |
* <code>/opt/lampp/bin/php cli.php mon_script -a test</code> |
* |
//Auteur original : |
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org> |
* @author Delphine CAUQUIL <delphine@tela-botanica.org> |
* @copyright Tela-Botanica 1999-2008 |
* @licence GPL v3 & CeCILL v2 |
* @version $Id$ |
*/ |
// +-------------------------------------------------------------------------------------------------------------------+ |
// Autoloader pour les namespaces, à base de routes |
function __autoload($nom_classe) { |
//echo "AUTOLOAD\n"; |
$dernierAS = strrpos($nom_classe, "\\"); |
$ns = substr($nom_classe, 0, $dernierAS); |
$nom = substr($nom_classe, strrpos($nom_classe, "\\") + 1); |
//echo "Recherche : $nom / $ns\n"; |
// Routes selon les namespaces |
$routes = array( |
'TelaBotanica\Del\Commun' => Config::get('cheminDelCommun') |
); |
if (array_key_exists($ns, $routes)) { |
//echo "Route trouvée: " . $routes[$ns] . "\n"; |
$fichier_a_inclure = dirname(__FILE__) . DS . $routes[$ns] . DS . $nom . '.php'; |
if (file_exists($fichier_a_inclure)) { |
include_once $fichier_a_inclure; |
return null; |
} |
} |
} |
// Le fichier Framework.php du Framework de Tela Botanica doit être appelée avant tout autre chose dans l'application. |
// Sinon, rien ne sera chargé. |
// Chemin du fichier chargeant le framework requis |
$framework = dirname(__FILE__).DIRECTORY_SEPARATOR.'framework.php'; |
if (!file_exists($framework)) { |
$e = "Veuillez paramétrer l'emplacement et la version du Framework dans le fichier $framework"; |
trigger_error($e, E_USER_ERROR); |
} else { |
// Inclusion du Framework |
require_once $framework; |
// Ajout d'information concernant cette application |
Framework::setCheminAppli(__FILE__);// Obligatoire |
Framework::setInfoAppli(Config::get('info')); |
// Initialisation et lancement du script appelé en ligne de commande |
Cli::executer(); |
} |
?> |
/branches/v1.2-lithium/scripts/. |
---|
New file |
Property changes: |
Added: svn:ignore |
+framework.php |