Subversion Repositories eFlore/Applications.del

Compare Revisions

Ignore whitespace Rev 1489 → Rev 1490

/trunk/services/modules/0.1/images/ListeImages2.php
22,8 → 22,8
* (cf requestFilterParams())
*
* Histoire: auparavant (pré-r142x) un AVG + GROUP BY étaient utilisés pour générer on-the-fly les valeurs
* utilsées ensuite pour l'ORDER BY. La situation à base de del_image_stat
* est déjà bien meilleur sans être pour autant optimale. cf commentaire de sqlAddConstraint()
* utilisées ensuite pour l'ORDER BY. La situation à base de del_image_stat
* est déjà bien meilleure sans être pour autant optimale. cf commentaire de sqlAddConstraint()
*
*
* Tags:
47,7 → 47,7
* (http://www.mysqlperformanceblog.com/2007/04/06/using-delayed-join-to-optimize-count-and-limit-queries/)
* - éviter de dépendre d'une jointure systématique sur `cel_obs`, uniquement pour `(date_)transmission
* (cf VIEW del_image)
* - réorganiser les méthodes statiques parmis Observation, ListeObservations et ListImages2
* - poursuivre la réorganisation des méthodes statiques parmis Observation, ListeObservations et ListImages2
* - *peut-être*: passer requestFilterParams() en méthode de classe
*
*
65,6 → 65,7
*
*/
 
require_once(dirname(__FILE__) . '/../DelTk.php');
require_once(dirname(__FILE__) . '/../observations/ListeObservations.php');
require_once(dirname(__FILE__) . '/../observations/Observation.php');
restore_error_handler();
83,7 → 84,7
 
static $tri_possible = array('date_transmission', 'date_observation', 'votes', 'tags');
 
// en plus de ceux dans ListeObservations
// en plus de ceux dans DelTk
static $parametres_autorises = array('protocole', 'masque.tag_cel', 'masque.tag_pictoflora', 'masque.milieu');
 
static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10,
151,32 → 152,35
// ($parametres_autorises) sauf... masque.type qui fait des modif' de WHERE sur les mots-clefs.
// Évitons ce genre de chose pour PictoFlora et les risques de conflits avec masque.tag
// même si ceux-ci sont improbables (pas d'<input> pour cela).
$params_ip = ListeObservations::requestFilterParams($parametres,
array_diff(ListeObservations::$parametres_autorises,
$params_ip = DelTk::requestFilterParams($parametres,
array_diff(DelTk::$parametres_autorises,
array('masque.type')),
$this->conteneur);
 
// notre propre filtrage sur l'INPUT
$params_pf = self::requestFilterParams($parametres,
array_merge(ListeObservations::$parametres_autorises,
array_merge(DelTk::$parametres_autorises,
self::$parametres_autorises));
 
/* filtrage des tags + sémantique des valeurs multiples:
Lorsqu'on utilise masque.tag* pour chercher des tags, ils sont
postulés comme séparés par des virgule, et l'un au moins des tags doit matcher. */
$params_pf['masque.tag_cel'] = self::buildTagsAST(@$parametres['masque.tag_cel'], 'OR', ',');
$params_pf['masque.tag_pictoflora'] = self::buildTagsAST(@$parametres['masque.tag_pictoflora'], 'OR', ',');
$params_pf['masque.tag_cel'] = DelTk::buildTagsAST(@$parametres['masque.tag_cel'], 'OR', ',');
$params_pf['masque.tag_pictoflora'] = DelTk::buildTagsAST(@$parametres['masque.tag_pictoflora'], 'OR', ',');
 
$params = array_merge(ListeObservations::$default_params, // paramètre par défaut Identiplante
self::$default_params, // paramètres par défaut PictoFlora
$params_ip, // les paramètres passés, traités par Identiplante
$params_pf); // les paramètres passés, traités par PictoFlora
$params = array_merge(
DelTk::$default_params, // paramètre par défaut Identiplante
self::$default_params, // paramètres par défaut PictoFlora
$params_ip, // les paramètres passés, traités par Identiplante
$params_pf); // les paramètres passés, traités par PictoFlora
 
// XXX: temp tweak
/* $this->conteneur->setParametre('url_images', sprintf($this->conteneur->getParametre('url_images'),
"%09d", $params['format']));*/
 
// création des contraintes (génériques, de ListeObservations)
// création des contraintes (génériques de DelTk)
DelTk::sqlAddConstraint($params, $db, $req);
// création des contraintes héritées de Identiplante (TODO: needed ??)
ListeObservations::sqlAddConstraint($params, $db, $req, $this->conteneur);
// création des contraintes spécifiques (sur les tags essentiellement)
self::sqlAddConstraint($params, $db, $req, $this->conteneur);
192,7 → 196,7
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats dans la table del_obs_images
if(!$idobs_tab) {
$resultat = new ResultatService();
$resultat->corps = array('entete' => ListeObservations::makeJSONHeader(0, $params, Config::get('url_service')),
$resultat->corps = array('entete' => DelTk::makeJSONHeader(0, $params, Config::get('url_service')),
'resultats' => array());
return $resultat;
/*
216,7 → 220,7
$images[$i] = $o->consulter(array($i), array('justthrow' => 1));
}
*/
list($images, $images_keyed_by_id_image) = ListeObservations::reformateImagesDoubleIndex(
list($images, $images_keyed_by_id_image) = self::reformateImagesDoubleIndex(
$liaisons,
$this->conteneur->getParametre('url_images'),
$params['format']);
237,7 → 241,7
$params_header = array_merge($params, array_filter(array('masque.tag_cel' => @$parametres['masque.tag_cel'],
'masque.tag_pictoflora' => @$parametres['masque.tag_pictoflora'])));
$resultat = new ResultatService();
$resultat->corps = array('entete' => ListeObservations::makeJSONHeader($total, $params_header, Config::get('url_service')),
$resultat->corps = array('entete' => DelTk::makeJSONHeader($total, $params_header, Config::get('url_service')),
'resultats' => $images);
return $resultat;
}
292,7 → 296,7
*
*/
static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
// TODO implement dans ListeObservations ?
// TODO implement dans DelTk ?
if(!empty($p['masque.milieu'])) {
$req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%');
}
440,8 → 444,8
}
 
static function chargerImages($db, $idImg) {
$obs_fields = Observation::sqlFieldsToAlias(self::$mappings['observations'], NULL);
$image_fields = Observation::sqlFieldsToAlias(self::$mappings['images'], NULL);
$obs_fields = DelTk::sqlFieldsToAlias(self::$mappings['observations'], NULL);
$image_fields = DelTk::sqlFieldsToAlias(self::$mappings['images'], NULL);
return $db->recupererTous(sprintf('SELECT '.
' CONCAT(id_image, "-", id_observation) AS jsonindex,'.
484,17 → 488,18
'ordre' => $p['ordre']);
 
$or_masque = array_merge(
ListeObservations::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
DelTk::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
self::requestFilterParams($or_params));
 
/* Lorsqu'on utilise le masque général pour chercher des tags, ils sont
postulés comme séparés par des espaces, et doivent être tous matchés. */
$or_masque['masque.tag_cel'] = self::buildTagsAST($p['masque'], 'AND', ' ');
$or_masque['masque.tag_pictoflora'] = self::buildTagsAST($p['masque'], 'AND', ' ');
$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
 
 
// pas de select, groupby & co ici: uniquement 'join' et 'where'
$or_req = array('join' => array(), 'where' => array());
DelTk::sqlAddConstraint($or_masque, $db, $or_req);
ListeObservations::sqlAddConstraint($or_masque, $db, $or_req);
self::sqlAddConstraint($or_masque, $db, $or_req);
 
506,7 → 511,39
}
}
 
// complete & override ListeObservations::requestFilterParams() (même usage)
 
// cf Observation::reformateObservationSimpleIndex() et ListeObservations::reformateObservation()
// (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...)
static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') {
// XXX: cf Observation.php::consulter(), nous pourriouns ici
// conserver les valeurs vides (pour les phptests notamment, ou non)
// $obs = array_map('array_filter', $obs);
$obs_merged = $obs_keyed_by_id_image = array();
foreach($obs as $o) {
// ceci nous complique la tâche pour le reste du processing...
$id = $o['jsonindex'];
// ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs)
// et celui indexé par simple id_image qui est fort utile pour mapVotesToImages()
// mais tout deux partage leur référence à "protocole"
$image = array(
'id_image' => $o['id_image'],
'binaire.href' => sprintf($url_pattern, $o['id_image'], $image_format),
'mots_cles_texte' => @$o['i_mots_cles_texte'], // @, peut avoir été filtré par array_map() ci-dessus
);
unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']);
if(!isset($obs_merged[$id])) $obs_merged[$id] = $image;
$obs_merged[$id]['observation'] = $o;
$obs_merged[$id]['protocoles_votes'] = array();
$obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] = &$obs_merged[$id]['protocoles_votes'];
}
 
return array($obs_merged,$obs_keyed_by_id_image);
}
 
 
 
// complete & override DelTk::requestFilterParams() (même usage)
static function requestFilterParams(Array $params, $parametres_autorises = NULL) {
if($parametres_autorises) { // filtrage de toute clef inconnue
$params = array_intersect_key($params, array_flip($parametres_autorises));
513,8 → 550,8
}
 
$p = array();
$p['tri'] = ListeObservations::unsetIfInvalid($params, 'tri', self::$tri_possible);
$p['format'] = ListeObservations::unsetIfInvalid($params, 'format', self::$format_image_possible);
$p['tri'] = DelTk::unsetIfInvalid($params, 'tri', self::$tri_possible);
$p['format'] = DelTk::unsetIfInvalid($params, 'format', self::$format_image_possible);
 
// "milieu" inutile pour IdentiPlantes ?
if(isset($params['masque.milieu'])) $p['masque.milieu'] = trim($params['masque.milieu']);
536,64 → 573,7
}
 
 
/* Construit un (vulgaire) abstract syntax tree:
"AND" => [ "tag1", "tag2" ]
Idéalement (avec un parser simple comme proposé par http://hoa-project.net/Literature/Hack/Compiler.html#Langage_PP)
nous aurions:
"AND" => [ "tag1", "tag2", "OR" => [ "tag3", "tag4" ] ]
 
Ici nous devons traiter les cas suivants:
tags séparés par des "ET/AND OU/OR", séparés par des espaces ou des virgules.
Mais la chaîne peut aussi avoir été issue du "masque général" (la barre de recherche générique).
ce qui implique des comportement par défaut différents afin de préserver la compatibilité.
 
Théorie:
1) tags passés par "champ tag":
- support du ET/OU, et explode par virgule.
- si pas d'opérande détectée: "OU"
 
2) tags passés par "recherche générale":
- support du ET/OU, et explode par whitespace.
- si pas d'opérande détectée: "ET"
 
La présence de $additional_sep s'explique car ET/OU sous-entendent une séparation par des espaces.
Mais ce n'est pas toujours pertinent car: 1) la compatibilité suggère de considérer parfois
la virgule comme séparateur et 2) les tags *peuvent* contenir des espaces. Par conséquent:
* a,b,c => "a" $default_op "b" $default_op "c"
* a,b AND c => "a" AND "b" AND "c"
* a OR b AND c,d => "a" AND "b" AND "c" AND "d"
C'est à dire par ordre décroissant de priorité:
1) opérande contenu dans la chaîne
2) opérande par défaut
3) les séparateurs présents sont substitués par l'opérande déterminée par 1) ou 2)
 
// TODO: support des parenthèses, imbrications & co: "(", ")"
// http://codehackit.blogspot.fr/2011/08/expression-parser-in-php.html
// http://blog.angeloff.name/post/2012/08/05/php-recursive-patterns/
 
@param $str: la chaîne à "parser"
@param $default_op: "AND" ou "OR"
@param $additional_sep: séparateur de mots:
*/
static function buildTagsAST($str = NULL, $default_op, $additional_sep = ',') {
if(!$str) return;
$words = preg_split('/ (OR|AND|ET|OU) /', $str, -1, PREG_SPLIT_NO_EMPTY);
 
if(preg_match('/\b(ET|AND)\b/', $str)) $op = 'AND';
elseif(preg_match('/\b(OU|OR)\b/', $str)) $op = 'OR';
else $op = $default_op;
 
if($additional_sep) {
array_walk($words,
create_function('&$v, $k, $sep', '$v = preg_split("/".$sep."/", $v, -1, PREG_SPLIT_NO_EMPTY);'),
$additional_sep);
}
$words = self::array_flatten($words);
$words = array_map('trim', $words);
return array($op => array_filter($words));
}
 
 
// met à jour *toutes* les stats de nombre de tags et de moyenne des votes
static function _update_statistics($db) {
$db->requeter("TRUNCATE TABLE del_image_stat");
616,15 → 596,4
static function revOrderBy($orderby) {
return $orderby == 'asc' ? 'desc' : 'asc';
}
 
static function array_flatten($arr) {
$arr = array_values($arr);
while (list($k,$v)=each($arr)) {
if (is_array($v)) {
array_splice($arr,$k,1,$v);
next($arr);
}
}
return $arr;
}
}