Rev 382 | Rev 385 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
<?php/*** Indexation dans Algolia des référentiels** Description : formate les données des référentiels choisis et envoie tout ça* dans Algolia** Utilisation : php script.php algolia [-ref "ref1,ref2,..."]* -ref (optionnel): liste de codes de référentiels séparés par des virgules;* par défaut: "apd,bdtfx,bdtxa,isfan"** Exemples:* php script.php algolia* php script.php algolia -ref "bdtfx,isfan"** @note: ignorer le paramètre fasciste -a : on ne s'en sert pas** @author Tela Botanica <equipe-dev@tela-botanica.org>* @licence GPL v3 & CeCILL v2*/restore_error_handler();restore_exception_handler();ini_set("display_errors","1");error_reporting(E_ALL);// composer autoloadrequire dirname(__FILE__) . '/../../../vendor/autoload.php';class Algolia extends ScriptCommande {const SCRIPT_NOM = 'algolia';public $parametres = array('-ref' => array(false, false, 'Celui qui lit ça est un con'));/** connexion PDO à la BDD "referentiels" */protected $bdd;/** client API Algolia */protected $algolia;protected $indexAlgolia;public function executer() {echo "Indexation des référentiels dans Algolia" . PHP_EOL;// Bibliothèque Algolia PHP pour appeler l'APIConfig::charger(dirname(__FILE__) . '/algolia.ini');$this->algolia = new \AlgoliaSearch\Client(Config::get('algolia_application_id'), Config::get('algolia_api_key'));$this->indexAlgolia = $this->algolia->initIndex(Config::get('algolia_index'));/*$settings = $this->indexAlgolia->getSettings();var_dump(json_encode($settings));exit;*/// Réglages de l'index @TODO tenir à jourif ($this->confirmer("Charger les réglages par défaut (index_settings.json) dans la configuration de l'index Algolia ?")) {// Chargement des réglages par défaut$reglagesJson = file_get_contents(dirname(__FILE__) . '/index_settings.json');$reglages = json_decode($reglagesJson, true);$this->indexAlgolia->setSettings($reglages);echo "Réglages chargés dans Algolia" . PHP_EOL;}// Connexion à la base$this->connecterPDO();// Liste des référentiels à fusionner$refsTexte = Config::get('algolia_referentiels');$refs = explode(",", $refsTexte);// Liste des référentiels à mettre à jour$refsMajTexte = $this->getParam("ref");if ($refsMajTexte === false) {// si le paramètre est vide, on met tout à jour$refsMaj = $refs;} else {$refsMaj = explode(",", $refsMajTexte);}// Déniaisage 1foreach ($refs as $k => $r) {$fichierRequete = dirname(__FILE__) . "/algolia_" . $r . ".sql";if (! file_exists($fichierRequete)) {echo "- fichier [$fichierRequete] non trouvé, fusion de [$r] ignorée" . PHP_EOL;unset($refs[$k]);}}if (empty($refs)) {echo "Aucun référentiel à fusionner" . PHP_EOL;exit;}// Déniaisage 2foreach ($refsMaj as $k => $r) {if (! in_array($r, $refs)) {echo "- le référentiel à mettre à jour [$r] n'est pas présent dans la liste à fusionner, il sera ignoré" . PHP_EOL;unset($refsMaj[$k]);}}if (empty($refsMaj)) {echo "Aucun référentiel à mettre à jour" . PHP_EOL;exit;}// Confirmationif (! $this->confirmer("Fusion des référentiels [" . implode(',', $refs) . "] et mise à jour de [" . implode(',', $refsMaj) . "]. Continuer ?")) {exit;}//var_dump($refs);$donneesBrutes = array();// Exécution des requêtes pour chaque référentielforeach ($refs as $ref) {$fichierRequete = dirname(__FILE__) . "/algolia_" . $ref . ".sql";// Exécution de la requête$requete = file_get_contents($fichierRequete);$resultat = $this->requete($requete);/*while ($ligne = $resultat->fetch()) {var_dump($ligne);break;}*/$donneesBrutes[$ref] = $resultat->fetchAll();// Info utilisation mémoire$mem = memory_get_usage(true);$memMio = round($mem / (1024 * 1024));echo "Mémoire utilisée : $memMio Mio" . PHP_EOL;}// Fusion !$index = $this->fusionnerReferentiels($donneesBrutes);//$this->extrait($index, array('Acacia dealbata Link','Acacia Mill.','Fabaceae'));// Mise en forme$index = $this->mettreEnForme($index);//$this->extrait($index, 3);// Stats$taille = count($index);echo "Taille de l'index: [$taille] lignes !" . PHP_EOL;//file_put_contents("couscous.json", json_encode($index));// Calcul des différences ?// Insertion ?$this->insererDansAlgolia($index);// Info utilisation mémoire totale$mem = memory_get_peak_usage(true);$memMio = round($mem / (1024 * 1024));echo "Mémoire maximale utilisée : $memMio Mio" . PHP_EOL;}/*** Génère un index unique pour Algolia à partir des données de n référentiels*/protected function fusionnerReferentiels(&$donneesRefs) {$index = array();foreach ($donneesRefs as $ref => &$d) {$nbTaxons = count($d);echo "-- fusion du référentiel [$ref] : $nbTaxons taxons --" . PHP_EOL;$fusions = 0;foreach ($d as $taxon) {$nomSci = $taxon[$ref . '_nom_sci'];//$nn = $taxon[$ref . '_num_nom'];// Ajout du nom d'auteur pour éviter les collisions dans un même référentielif (! empty ($taxon[$ref . '_auteur'])) {$nomSci .= ' ' . $taxon[$ref . '_auteur'];}// -- ÉLIMINATION DES NOMS SANS CORRESPONDANCEif (empty($taxon[$ref . '_num_nom_retenu'])) {//echo "XX élimination du nom sans correspondance : [$nomSci] (nn $nn)" . PHP_EOL;continue;}if (! isset($index[$nomSci])) {$index[$nomSci] = array('objectID' => $nomSci,'referentiels' => array());} else {//echo "> fusion sur [$nomSci] (nn $nn)" . PHP_EOL;$fusions++;}$index[$nomSci] = array_merge($index[$nomSci], $taxon);$index[$nomSci]['referentiels'][] = $ref;//break;}$taille = count($index);echo "- taille de l'index après ajout de [$ref]: [$taille] lignes ($fusions fusions)" . PHP_EOL;}return $index;}/*** Organise les données de chaque objet conformément à la structure de* l'index Algolia** Voir commentaires sur cette page :* http://taiga.tela-botanica.net/project/mathias-site-web/task/75** L'objectID est le MD5 de la "clef" (nom scientifique avec auteur)*/protected function mettreEnForme($index) {$nouvelIndex = array();foreach ($index as $nomSci => $taxon) {$nouveauTaxon = array('objectID' => md5($nomSci),'referentiels' => $taxon['referentiels']);foreach ($taxon['referentiels'] as $ref) {// ingrédients$nn = $taxon[$ref . '_num_nom'];$ns = $taxon[$ref . '_nom_sci'];$nts = $taxon[$ref . '_num_tax_sup'];$rang = $taxon[$ref . '_rang'];$auteur = $taxon[$ref . '_auteur'];$annee = $taxon[$ref . '_annee'];$biblio = $taxon[$ref . '_biblio'];$nom_supra_generique = $taxon[$ref . '_nom_supra_generique'];$genre = $taxon[$ref . '_genre'];$epithete_sp = $taxon[$ref . '_epithete_sp'];$type_epithete = $taxon[$ref . '_type_epithete'];$epithete_infra_sp = $taxon[$ref . '_epithete_infra_sp'];$cultivar = $taxon[$ref . '_cultivar'];$cultivar_groupe = $taxon[$ref . '_cultivar_groupe'];$nomCommun = (isset($taxon[$ref . '_nom_francais']) ? $taxon[$ref . '_nom_francais'] : '');$url = $taxon[$ref . '_url'];$synonymes = json_decode($taxon[$ref . '_synonymes'], true);$raccourcis = json_decode($taxon[$ref . '_shortcuts'], true);$raccourcis = ($raccourcis != null ? array_values(array_unique($raccourcis)) : null); // array_values réindexe pour obtenir une liste en JSON et non un objet// garniture$donneesRef = array('nomenclatural_number' => intval($nn),'scientific_name' => $ns,'common_name' => $nomCommun,'synonyms' => $synonymes,'url' => $url,'parent_taxon_number' => intval($nts),'rank' => intval($rang),'author' => $auteur,'year' => intval($annee),'biblio' => $biblio,'supra_genus_name' => $nom_supra_generique,'genus' => $genre,'species_attribute' => $epithete_sp,'attribute_type' => $type_epithete,'infra_species_attribute' => $epithete_infra_sp,'cultivar' => $cultivar,'cultivar_groupe' => $cultivar_groupe);$nouveauTaxon[$ref] = $donneesRef;$nouveauTaxon['shortcuts'] = $raccourcis;}$nouvelIndex[] = $nouveauTaxon;}return $nouvelIndex;}/*** Appelle l'API Algolia pour indexer les données présentes dans $index, par* tranches.*/protected function insererDansAlgolia(&$index) {$tailleTranche = 5000;echo "++++ Insertion dans Algolia (" . count($index) . " objets) !! ++++" . PHP_EOL;// insertion par tranches pour éviter un timeout sur l'API Algoliawhile (count($index) > 0) {echo "++ insertion d'une tranche de $tailleTranche... (" . count($index) . " restant)" . PHP_EOL;$tranche = array_splice($index, 0, $tailleTranche);//var_dump($tranche);$this->indexAlgolia->addObjects($tranche);}}// ---------------- utilitaires --------------------------------------------protected function extrait($index, $clefsOuNombre) {// Debugecho PHP_EOL . "---- extrait des données --" . PHP_EOL;if (is_array($clefsOuNombre)) {foreach ($clefsOuNombre as $k) {var_dump($index[$k]);}} else {for ($i=0; $i < $clefsOuNombre; $i++) {var_dump($index[$i]);}}}protected function connecterPDO() {Config::charger(dirname(__FILE__) . '/../../configurations/bdd.ini');try {$dsn = Config::get('bdd_type').':dbname='.Config::get('bdd_nom').';host='.Config::get('bdd_hote');$this->bdd = new PDO($dsn, Config::get('bdd_utilisateur'), Config::get('bdd_mot_de_passe'));// Passe en UTF-8 la connexion à la BDD$this->bdd->exec("SET NAMES 'utf8'");// Affiche les erreurs détectées par PDO (sinon mode silencieux => aucune erreur affiché)$this->bdd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);} catch (PDOException $e) {//print_r($e);echo 'La connexion à la base de données via PDO a échoué : ' . $e->getMessage() . PHP_EOL;exit;}}protected function requete($requete) {$infos = null;try {$infos = $this->bdd->query($requete, PDO::FETCH_ASSOC);/*if ($infos === false) {echo $requete;}*/} catch (PDOException $e) {echo sprintf($e->getFile(), $e->getLine(), $e->getMessage(), $e->getCode(), $requete);}return $infos;}/*** Demande confirmation, et sort du script à moins qu'on tape ce qui est* indiqué (par défaut "o" pour "oui")*/protected function confirmer($question='Continuer ?', $codeAcceptation='o', $messageAnnulation='annulation') {echo $question . ' ("' . $codeAcceptation . '" pour confirmer, autre chose pour annuler)' . PHP_EOL;$handle = fopen ("php://stdin","r");$line = fgets($handle);if(strtolower(trim($line)) != strtolower($codeAcceptation)) {echo $messageAnnulation . PHP_EOL;return false;}fclose($handle);return true;}}?>