'fleur', 'fruit' => 'fruit', 'leaf' => 'feuille', 'bark' => 'écorce', 'branch' => 'branche', 'habit' => 'port', ]; // Différents types de lifecle possible pour filtrer les obs // Modified renvoi les obs en fonction de leur date de modif // Created, en fonction de la date de création // Deleted, well, you already got it protected $lifecycles = [ 'created', 'modified', 'deleted', // deleted peut être très lent (pas d'index coté PN pour le moment) ]; /** * Paramêtres par défaut disponibles pour la ligne de commande * le tableau se construit de la forme suivante : * - clé = nom du paramêtre '-foo' * - value = contient un nouveau tableau composé de cette façon : * - booléen: true si le paramêtre est obligatoire * - booléen ou var : true si le paramêtre nécessite un valeur à sa suite ou la valeur par défaut * - string: description du contenu du paramêtre * Les paramêtres optionels devraient être déclaré à la fin du tableau. * Le dernier parametre du tableau peut avoir la valeur '...', * il contiendra alors l'ensemble des paramêtres suivant trouvés sur la ligne de commande. * @var array */ protected $parametres_autorises = array( '-startDate' => array(false, true, 'Date de début (parsée par strtotime). Ex: "YYYY:MM:DD HH:II:SS"'), '-lifecycle' => array(false, true, 'Par défaut : modified ; Au choix parmi : created/modified/deleted (deleted est lent)'), '-pnObsId' => array(false, true, "ID de l'obs chez PlantNet"), '-project' => array(false, true, "projectId de l'obs chez PlantNet"), '-tbObsId' => array(false, true, "ID de l'obs chez Tela"), '-debug' => array(false, false, "Mode débug (ajoute du log) Au choix parmi : debug/log (log est plus verbeux)"), ); public function __construct($script_nom, $parametres_cli) { parent::__construct($script_nom, $parametres_cli); $this->bdd = new Bdd(); $this->correspondingIdsFilename = Config::get('correspondingIdsFilename'); $this->debug = $this->getParametre('debug'); } public function executer() { $cmd = $this->getParametre('a'); switch ($cmd) { case 'updateLatest': $this->updateLatest($this->getParametre('startDate'), $this->getParametre('lifecycle')); break; case 'updateOnePN': $this->updateOnePN((int)$this->getParametre('pnObsId'), $this->getParametre('project')); break; case 'updateOneTB': $this->updateOneTB((int)$this->getParametre('tbObsId')); break; case 'updateCorrespondingIdsTable': $this->updateCorrespondingIdsTable(); break; default: $msg = "Erreur : la commande '$cmd' n'existe pas!\n" . ', utilisez plutôt :' . "\n" . 'php cli.php PullPlantnet -a updateLatest -startDate "YYYY:MM:DD HH:II:SS" [[-lifecycle [modified]/created/deleted]]' . "\n" . 'php cli.php PullPlantnet -a updateOnePN -pnObsId "1234567890" -project "xxxxxx"' . "\n" . 'php cli.php PullPlantnet -a updateOneTB -tbObsId "1234567890"' . "\n" . 'php cli.php PullPlantnet -a updateCorrespondingIdsTable' . "\n" ; throw new Exception($msg); } } /** * Considering all modified pn obs since startDate * Inserting obs in tb db (if new and matching user email) */ private function updateLatest(string $startDate = '1917:03:15 00:00:00', string $lifecycle = 'modified') { $startDate = strtotime($startDate) * 1000; // we need a microtimestamp do { $observations_PN = $this->getLatest($startDate, $lifecycle); $this->updateObs($observations_PN); } while ($this->hasMore); } private function getLatest(int $startDate, string $lifecycle): array { $this->refreshBddConnexion(); if (!$this->currentPage) { // hop, on appelle le service de PN $url_service = str_replace( ['{token}', '{startDate}', '{lifecycle}'], [Config::get('tokenPlantnet'), $startDate, $lifecycle], Config::get('urlPlantnetBase').Config::get('urlPlantnetLatestChanges') ); $this->debug("URL service derniers changements : $url_service"); $this->currentPage = $url_service; } $ch = curl_init($this->currentPage); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $reponse = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $temps = curl_getinfo($ch, CURLINFO_TOTAL_TIME); curl_close($ch); $this->debug('Temps total en secondes : '.$temps); // cool, maintenant on va trier ce qu'on a reçu if (!$reponse) { throw new Exception("\nPN ne répond rien à l'adresse {$this->currentPage}\n"); } elseif (200 != $code) { // l'api répond avec une 404 quand y'a une date dans le futur ou simplement pas de nouvelle obs... if (404 == $code && strpos($reponse, 'No more results')) { $this->log("Pas d'autres résultats"); $this->hasMore = false; $this->currentPage = ''; return []; } throw new Exception("\nPN répond avec une $code à l'adresse {$this->currentPage} : $reponse\n"); } $responseJson = json_decode($reponse, true); $observations_PN = $responseJson['data'] ?? []; $this->hasMore = $responseJson['hasMore']; $this->lastTimestamp = end($observations_PN)['dateUpdated']; if ($this->hasMore) { // $url_service = Config::get('urlPlantnetBase').$responseJson['next']; // Build next page url with last read timestamp, to avoid pagination latency $url_service = str_replace( ['{token}', '{startDate}', '{lifecycle}'], [Config::get('tokenPlantnet'), $this->lastTimestamp, $lifecycle], Config::get('urlPlantnetBase').Config::get('urlPlantnetLatestChanges') ); $this->currentPage = $url_service; $this->debug("URL service derniers changements, page suivante : {$this->currentPage}"); } else { $this->currentPage = ''; } return $observations_PN; } private function getProjects(): array { // get PN projects list (useless since we have a single source) $url = str_replace('{token}', Config::get('tokenPlantnet'), Config::get('urlPlantnetBase').Config::get('urlPlantnetProjects')); $this->debug("URL service liste projets : $url"); $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $reponse = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if (!$reponse || 200 != $code) { throw new Exception("\nPN répond avec une $code à l'adresse $url : $reponse\n"); } $pnProjects = json_decode($reponse, true); echo 'Liste de projets récupérée : ' . count($pnProjects) . " projets dans la liste\n"; $this->debug("Liste projets : " . json_encode($pnProjects)); return $pnProjects; } /** * For a given bunch of plantnet obs, post new one threw saisie widget service */ private function updateObs(array $observations_PN) { $url_cel_widget_saisie = Config::get('urlCelWidgetSaisie'); foreach ($observations_PN as $obs) { // on ne teste pas la suppression de l'obs ici, faut le faire après avoir vérifié si on l'a déjà synchro if (!isset($obs['currentName'])) { $this->log("Obs {$obs['id']} sans nom de taxon, on zap"); continue; // pas de nom de taxon, obs inutile } if (!isset($obs['geo'])) { $this->log("Obs {$obs['id']} sans geom, on zap"); continue; // pas de position, obs inutile } if (!isset($obs['dateObs'])) { $this->log("Obs {$obs['id']} sans date, on zap"); continue; // pas de date, obs inutile } if (isset($obs['partner']['id']) && 'tela' === $obs['partner']['id']) { // c'est une obs tela // si c'est mis à jour récemment coté PN et qu'on l'a pas supprimée chez nous entre temps // on update les votes PN // on update l'identification proposée par PN // @TODO $this->log("Obs {$obs['id']} venant de Tela, mise à jour pas implémentée, on zap"); continue; } elseif (!isset($obs['partner']['id'])) { // ce n'est pas une obs d'un partenaire, c'est donc une obs PN // on récupère l'id utilisateur tela via son mail $email = $obs['author']['email']; $infos_utilisateur = $this->findUserInfo($email); if (!$infos_utilisateur) { $this->log("Obs {$obs['id']} email $email ne provient pas d'un telabotaniste"); continue; // c'est pas un telabotaniste } $this->log("Obs {$obs['id']} email $email provient d'un telabotaniste"); $this->log(json_encode($obs)); // die(var_dump($obs)); // vérification que l'obs n'a pas déjà été ajoutée à la BdD de Tela $sql = "SELECT date_maj, id_observation FROM cel_plantnet WHERE id_plantnet = '{$obs['id']}'" . ' -- ' . __FILE__ . ':' . __LINE__; $res = $this->bdd->recupererTous($sql); // die(var_dump($res)); if ($res) { // l'obs existe déjà, on vérifie si il faut màj puis on passe à la suite // la date de l'obs est un microtime, donc on coupe les millièmes // die(var_dump((int)($obs['dateUpdated']/1000), strtotime($res[0]['date_maj']))); if ((int)($obs['dateUpdated']/1000) > strtotime($res[0]['date_maj'])) { echo "Obs déjà en base, mise à jour : ID PN {$obs['id']} ; ID TB {$res[0]['id_observation']} ; utilisateur_tb $email\n"; $this->updateFromPN($obs, $res[0]['id_observation']); } else { echo "Obs déjà en base, déjà à jour : ID PN {$obs['id']} ; ID TB {$res[0]['id_observation']} ; utilisateur_tb $email\n"; } continue; } if (isset($obs['deleted']) && (true === $obs['deleted'])) { $this->log("Obs {$obs['id']} supprimée coté PlantNet"); continue; // obs supprimée chez PN sans être passée par nos serveurs } $images = []; $tags_images = []; $images_size = 0; foreach ($obs['images'] ?? [] as $i => $image) { if ($image['deleted']) { $this->log("Obs {$obs['id']} image {$image['id']} supprimée coté PlantNet"); continue; } // téléchargement de l'image PN $img = false; $retry = 3; do { $img = file_get_contents($image['o']); if (!$img) { $retry--; $this->log("Obs {$obs['id']} lecture image {$image['id']} tentatives restantes : $retry"); } } while (!$img && $retry); if (!$img) { echo "Abandon, image impossible à télécharger : {$image['o']}\n"; continue; } // Écriture dans un fichier temporaire $tempfile = tempnam("/tmp", "PullPN_") . ".jpg"; $handle = fopen($tempfile, "w"); fwrite($handle, $img); fclose($handle); $images_size += filesize($tempfile); $this->log("Obs {$obs['id']} image {$image['id']} créée " . number_format(filesize($tempfile), 0, ',', ' ') . " octets : $tempfile"); $params = [ 'name' => 'image' . $i, 'fichier' => new CURLFile($tempfile, 'image/jpg') ]; // envoi de l'image PN à l'api de création $ch = curl_init(Config::get('urlCelUploadImageTemp')); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $params); $reponse = curl_exec($ch); $this->log("Obs {$obs['id']} image {$image['id']} envoyée à l'api de création. Réponse : $reponse"); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); unlink($tempfile); $reponse = simplexml_load_string($reponse); /* Response format if success: https://api-test.tela-botanica.org/tmp/cel/imgtmp/chaflipe_min.jpg chaflipe.jpg */ // var_dump($reponse); if ($reponse && '' == $reponse->message /* && '' != $reponse->{'image-nom'} */) { $images[] = (string)$reponse->{'image-nom'}; $tag = $this->tagsImageTraduits[$image['organ']] ?? $image['organ']; if (trim($tag)) { $tags_images[] = $tag; } } } // var_dump($images, $tags_images); // die(); $geo = $this->getGeoInfo($obs['geo']); $this->log("Obs {$obs['id']} décodage du geom : " . json_encode($obs['geo'])); $this->log("Obs {$obs['id']} geom décodé : " . json_encode($geo)); // on insère l'obs via le service CelWidgetSaisie $infos_obs = [ 'obsId1[date]' => date('d/m/Y', intdiv($obs['dateObs'], 1000)), 'obsId1[latitude]' => $geo['lat'] ?? null, 'obsId1[longitude]' => $geo['lon'] ?? null, 'obsId1[pays]' => $geo['countryCode'] ?? null, 'obsId1[code_postal]' => $geo['postcode'] ?? null, 'obsId1[commune_nom]' => $geo['city'] ?? null, // 'obsId1[commune_code_insee]' => '', // 'obsId1[lieudit]' => '', // 'obsId1[milieu]' => '', 'obsId1[nom_sel]' => $obs['currentName'], // 'obsId1[nom_ret]' => '', // 'obsId1[famille]' => '', 'obsId1[certitude]' => 'douteux', // 'obsId1[notes]' => '', // 'obsId1[num_nom_ret]' => '', // 'obsId1[num_nom_sel]' => '', // 'obsId1[num_taxon]' => '', 'obsId1[referentiel]' => $this->findProbableTaxoRepos($obs['project']), // @TODO faire une fois et mettre en cache // 'obsId1[station]' => '', 'obsId1[obs_id]' => $obs['id'], 'projet' => 'PlantNet', 'tag-img' => implode(', ', $tags_images ?? []), 'tag-obs' => 'plantnet, plantnet-mobile, pn:referentiel-' . $obs['project'], 'utilisateur[courriel]' => $email, 'utilisateur[id_utilisateur]' => $infos_utilisateur['id'], 'utilisateur[nom]' => $infos_utilisateur['nom'], 'utilisateur[prenom]' => $infos_utilisateur['prenom'], 'origin' => 'pullPlantnet', ]; foreach ($images as $i => $image) { $infos_obs["obsId1[image_nom][$i]"] = $image; } $this->log("Obs {$obs['id']} prête à être insérée : " . json_encode($infos_obs)); // curl post $infos_obs $ch = curl_init($url_cel_widget_saisie); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $infos_obs); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $reponse = curl_exec($ch); $this->log("Obs {$obs['id']} réponse de la requête d'insertion : " . json_encode($reponse)); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if (!$reponse || 200 != $code) { throw new Exception("Ça a pété à l'envoi de l'obs :\n $reponse\n avec l'obs : " . json_encode($infos_obs) . "\n"); } $id_obs_tb = json_decode($reponse, true)['id']; $id_obs_pn = $obs['id']; // on insère les ids de correspondance obsPN obsTB dans la table cel_plantnet $sql = 'INSERT INTO cel_plantnet (id_observation, id_plantnet, date_maj)' .' VALUES (%s, %s, NOW())' . ' -- ' . __FILE__ . ':' . __LINE__; $sql = sprintf($sql, $this->bdd->proteger($id_obs_tb), $this->bdd->proteger($id_obs_pn)); $this->bdd->requeter($sql); $date = date('d/m/Y H:i:s', intdiv($obs['dateObs'], 1000)); $count_img = count($images); $images_size = number_format($images_size, 0, ',', ' '); echo "Obs insérée en base : ID PN $id_obs_pn ; ID TB $id_obs_tb ; utilisateur_tb $email ; date_obs $date ; images $count_img ; taille_octets $images_size \n"; } } } /** * Return TB taxo repos names for given PN project * * @return string */ private function findProbableTaxoRepos($pnProjectId) { $selectedRepos = []; // Référentiels tela botanica indexés par référentiels PN (version octobre 2019) // @TODO à revoir en fonction des nouvelles mises en prod de nouveaux référentiels $tbTaxoRepos = [ 'afn' => [/*'apd', */'isfan'], 'aft' => ['apd'], 'antilles' => ['bdtxa', 'taxref'], 'canada' => ['vascan'], 'comores' => ['apd'], 'endemia' => ['florical', 'taxref'], 'taxref ' => ['bdtfx'], 'guyane' => ['aublet', 'taxref'], 'hawai' => [], 'lapaz' => ['aublet', 'taxref'], 'martinique' => ['bdtxa', 'taxref'], 'maurice' => ['apd'], 'medor' => ['lbf'], 'namerica' => [], 'polynesiefr' => ['taxref'], 'prosea' => ['asia'], 'prota' => ['isfan', 'apd'], 'provence' => ['bdtfx', 'taxref'], 'reunion' => ['bdtre', 'taxref'], 'salad' => ['bdtfx', 'taxref'], 'useful' => [], 'weurope' => ['bdtfx'], 'the-plant-list' => [], 'iscantree' => ['apd'], 'central-america' => [], 'invasion' => ['bdtfx'], 'weeds' => ['bdtfx'], 'malaysia' => [], ]; if (array_key_exists($pnProjectId, $tbTaxoRepos)) { array_map(function($repo) use ($selectedRepos) { $selectedRepos[] = $repo; }, $tbTaxoRepos[$pnProjectId]); } // @TODO chercher le nom dans plusieurs référentiels avant d'envoyer l'obs // prendre "enrichirDonneesTaxonomiques" du service de saisie et le coller ici // en attendant on envoi juste le premier référentiel trouvé return $selectedRepos[0] ?? ''; } /* * Retrouve les infos d'un utilisateur tela à partir de son email * @param string $email User email * @return mixed Tableau des infos de l'utilisateur * False si l'email n'est pas trouvé dans l'annuaire */ private function findUserInfo($email) { // cherche dans le cache if (array_key_exists($email, $this->userInfos)) { $infosUtilisateur = $this->userInfos[$email]; // echo("Email in cache : $email\n"); } else { $infosUtilisateur = false; $urlInfosUtilisateur = str_replace('{email}', $email, Config::get('urlAnnuaireIdParEmail')); $reponse = false; $retry = 3; do { $ch = curl_init($urlInfosUtilisateur); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $reponse = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $retry--; if (!$reponse) { sleep(1); $this->log("Echec annuaire pour la requête $urlInfosUtilisateur tentatives restantes : $retry"); } } while (!$reponse && $retry); if (!$reponse) { echo "Abandon, l'annuaire semble injoignable, impossible à télécharger : $urlInfosUtilisateur\n"; return false; } // on peut pas tester le code de réponse de cette api, si l'utilisateur n'est pas trouvé ça fait une 500 >_< // une bonne réponse ressemble à ça : // {"killian@tela-botanica.org":{"id":"30489","prenom":"Killian","nom":"Stefanini","pseudo":"Killian Stefanini","pseudoUtilise":true,"intitule":"Killian Stefanini","avatar":"\/\/www.gravatar.com\/avatar\/a9b9b8484076540924c03af816c77fc8?s=50&r=g&d=mm","groupes":{"19226":"adm","18943":"","23152":"","21684":"","21598":"adm","23184":"","23516":""},"permissions":["editor"],"nomWiki":"KillianStefanini"}} $reponse = json_decode($reponse, true); if (!isset($reponse)) { throw new Exception("\nL'annuaire n'a pas répondu avec du json : code $code à l'adresse $urlInfosUtilisateur : $reponse\n"); } if (!isset($reponse['error'])) { $infosUtilisateur = array_shift($reponse); } // met en cache $this->userInfos[$email] = $infosUtilisateur; // echo("Email cached : $email\n"); } return $infosUtilisateur; } private function getGeoInfo($obs) { $geo = []; if (!isset($obs['lat']) && !isset($obs['lon'])) { return $geo; } // $data = [ // 'hitsPerPage' => 1, // 'aroundLatLng' => "{$obs['lat']},{$obs['lon']}" // ]; $headers = [ 'X-Algolia-Application-Id' => Config::get('algoliaApplicationId'), 'X-Algolia-API-Key' => Config::get('algoliaAPIKey'), ]; $lat = number_format($obs['lat'], 6, '.', ''); $lon = number_format($obs['lon'], 6, '.', ''); $ch = curl_init(Config::get('urlReverseGeocodingLatLng')."$lat,$lon"); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $reponse = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($reponse) { $reponse = json_decode($reponse, true); // die(var_dump($reponse)); $infos = $reponse['hits'][0]; $geo = [ 'lat' => (string) $obs['lat'], 'lon' => (string) $obs['lon'], 'country' => $infos['country']['fr'] ?? $infos['country']['default'], 'city' => $infos['city']['default'][0] ?? null, 'postcode' => $infos['postcode'][0] ?? null, 'countryCode' => strtoupper($infos['country_code']), ]; } return $geo; } private function updateFromPN(array $pnObs, string $tbObsId) { if (!is_array($pnObs) || !is_string($tbObsId)) { // die(var_dump($pnObs, $tbObsId)); throw new Exception("\nBad params types, give me an array and an integer plz\n"); } // die(var_dump($pnObs)); // log update date to cel_plantnet $sql = "UPDATE cel_plantnet SET date_maj = NOW() WHERE id_observation = $tbObsId" . ' -- ' . __FILE__ . ':' . __LINE__; $this->bdd->requeter($sql); // check for deleted if (isset($pnObs['deleted']) && (true === $pnObs['deleted'])) { // est-ce une obs issue de PN ? //// faut regarder le champ input_source == PlantNet $sql = "SELECT input_source FROM occurrence WHERE id = '$tbObsId'" . ' -- ' . __FILE__ . ':' . __LINE__; $res = $this->bdd->recupererTous($sql); if (isset($res[0]) && ('PlantNet' === $res[0]['input_source'])) { // oui ? alors supprimer obs ! echo "Obs supprimée coté PN, suppression : ID PN {$pnObs['id']} ; ID TB {$res[0]['id_observation']}\n"; $sql = "UPDATE photo SET occurrence_id = NULL WHERE occurrence_id = '$tbObsId'" . ' -- ' . __FILE__ . ':' . __LINE__; $this->bdd->requeter($sql); $sql = "DELETE FROM occurrence WHERE id = '$tbObsId'" . ' -- ' . __FILE__ . ':' . __LINE__; $this->bdd->requeter($sql); } } // check for deleted images foreach ($pnObs['images'] as $image) { if ($image['deleted']) { // no idea // y'a pas encore de lien tangible entre les images PN et les notres } } // @TODO : finir la comparaison des proposition entre celles de Tela et celles PN // // check for votes (notes et taxon) // $names = []; // foreach ($pnObs['computed']['votes'] as $vote) { // echo($vote['name']); // echo($vote['score']['pn']); // echo($vote['score']['total']); // $names[$vote['score']['pn']] = $vote['name']; // } // ksort($names, SORT_NUMERIC); // votes sorted by score asc // $pn_sorted_votes = array_reverse($names, true); // desc // // get existing votes // $sql = "SELECT id_commentaire, nom_sel, nom_sel_nn, nom_referentiel, nom_referentiel, utilisateur_courriel, proposition_initiale, proposition_retenue" // . "WHERE ce_observation = '$tbObsId' AND ce_commentaire_parent = 0" // . ' -- ' . __FILE__ . ':' . __LINE__; // $existing_votes = $this->bdd->recupererTous($sql); // // @TODO : compare votes // // insert_new_votes($pn_sorted_votes, $existing_votes); } private function updateOnePN($pnObsId, $pnProjectId) { if (!is_int($pnObsId) || !is_string($pnProjectId)) { throw new Exception("\nBad params types, give me an integer and a string plz\n"); } // get PN project list $list = []; $pnProjects = $this->getProjects(); // refresh projects list foreach ($pnProjects as $project) { $list[$project['id']] = $project['name']; } // if project not in list display list if (!array_key_exists($pnProjectId, $list)) { echo "Available projects:\n"; foreach ($list as $projectId => $projectName) { echo " - $projectId ($projectName)\n"; } throw new Exception("Project $pnProjectId does not exist\n"); } // get PN obs $urlInfosObs = str_replace( ['{token}', '{project}', '{id}'], [Config::get('tokenPlantnet'), $pnProjectId, $pnObsId], Config::get('urlPlantnetBase').Config::get('urlPlantnetObsById') ); $ch = curl_init($urlInfosObs); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if (!$response || 200 != $code) { throw new Exception("\nPlantnet api ($urlInfosObs) replied with code $code : $response\n"); } // change last modification date to force update $obs = json_decode($response, true); $date = new DateTime(); $date->setTimestamp(intdiv($obs['dateUpdated'], 1000) - 1); $date = $this->bdd->proteger($date->format('Y-m-d H:i:s')); $sql = "UPDATE cel_plantnet SET date_maj = $date WHERE id_plantnet = '$pnObsId'" . ' -- ' . __FILE__ . ':' . __LINE__; $this->bdd->requeter($sql); $this->updateObs([$obs]); } private function updateOneTB($id) { $corres = $this->findMatchingPartnerId($id, 'tb'); echo "L'obs tela avec l'id $id a été trouvée chez plantnet avec l'id : " .$corres['pn'] . "\n"; echo "La suite n'a pas été implémentée.\n"; } /* * Trouve les ids d'une obs chez les partenaires * Il faut lancer updateMatchingPartnersIds pour obtenir une liste fraiche * * @param string $id L'id de l'obs * @param string $partner Le partenaire correspondant à l'id de l'obs * * @return mixed Le tableau de correspondances d'ids * False si l'id n'est pas trouvé */ private function findMatchingPartnerId($id, $partner) { if (!in_array($partner, $this->allowedPartners)) { throw new Exception("\nUnknown partner : $partner. Available Partners : " . implode(', ', $this->allowedPartners) . "\n"); } // le json ressemble à ça : // {"tb":"2369732","pn":"1001020199"}, {"tb":"2369733","pn":"1001020176"}, ... $ids = json_decode(file_get_contents($this->correspondingIdsFilename), true); // extrait la colonne du partenaire recherché du tableau multidimensionnel fraichement décodé $partnerColumn = array_column($ids, $partner); // cherche l'index de l'id recherché $index = array_search($id, $partnerColumn); return $index ? $ids[$index] : false; } private function updateCorrespondingIdsTable() { // first update cache file if necessary if (!file_exists($this->correspondingIdsFilename) || time() > (filemtime($this->correspondingIdsFilename) + (24 * 3600))) { $this->updateMatchingPartnersIds(); } $data = file_get_contents($this->correspondingIdsFilename); $data = json_decode($data, true); $now = new DateTime; $now = $this->bdd->proteger($now->format('Y-m-d H:i:s')); $sqlInsert = 'INSERT INTO cel_plantnet (id_observation, id_plantnet, date_maj)' . ' VALUES %s' . ' ON DUPLICATE KEY UPDATE id_observation=VALUES(id_observation), id_plantnet=VALUES(id_plantnet)' . ' -- ' . __FILE__ . ':' . __LINE__; $values = ''; echo "Updating matching partners ids table\n"; foreach ($data as $id => $corres) { $id_obs_tb = $this->bdd->proteger($corres['tb']); $id_obs_pn = $this->bdd->proteger($corres['pn']); // on insère les ids de correspondance obsPN obsTB dans la table cel_plantnet $values .= " ($id_obs_tb, $id_obs_pn, $now),"; if (0 === $id % 100) { $values = substr($values, 0, -1); // retire la dernière virgule $sql = sprintf($sqlInsert, $values); // var_dump($sql); $this->bdd->requeter($sql); $values = ''; } } // Faut envoyer les dernières données qui sont pas passées dans le modulo $values = substr($values, 0, -1); // retire la dernière virgule $sql = sprintf($sqlInsert, $values); // var_dump($sql); $this->bdd->requeter($sql); $count = count($data); echo "Success: #$count updated\n"; } private function updateMatchingPartnersIds() { // curl -X GET --header 'Accept: application/json' 'https://bourbonnais.cirad.fr:8081/v1/edi/observations/partnerids?token=f0ca433febe320675c24ee2ddfab8b82f65d556b' > partnerids.txt $matchingPartnersIdsServiceUrl = str_replace( '{token}', Config::get('tokenPlantnet'), Config::get('urlPlantnetBase').Config::get('urlPlantnetMatchingPartner') ); $ch = curl_init($matchingPartnersIdsServiceUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); echo "Updating matching partners ids...\n"; $reponse = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if (!$reponse || 200 != $code) { throw new Exception("\nError updating partners ids, http error code \"$code\" whith url \"$matchingPartnersIdsServiceUrl\"...\n"); } echo "Response data from PN api gathered, know computing...\n"; if (!file_put_contents($this->correspondingIdsFilename, $reponse)) { throw new Exception("\nError writing correspondingIdsFilename with path : " . $this->correspondingIdsFilename . "\n"); } echo "Matching partners ids saved!"; } private function debug($text) { if ($this->debug) { echo 'DEBUG - ' . $text . "\n"; } } private function log($text) { if ('log' == $this->debug) { echo 'LOG - ' . $text . "\n"; } } private function refreshBddConnexion() { $this->bdd = null; $this->log("Fermeture et création d'une nouvelle connexion à la BdD"); $this->bdd = new Bdd(); } }