8768 sujets

Développement web côté serveur, CMS

Bonsoir,

je ne connais pas très bien les tâches Crons en général et encore moins le cron de WordPress (WP_CRON)

Est il possible de faire une tâche cron sous WordPress qui par exemple se connecterait toutes les 3 ou 4 heures à une API de données météo pour récupérer des données et mettre à jour une option de la table wp_options.

Ainsi, sur le site Wordpress il suffirait de lire la table wp_options pour afficher les données, sans avoir à faire une requête sur l'API qui retarde le chargement de la page.
Modifié par lionel_css3 (28 Aug 2024 - 21:45)
Modérateur
Salut Lionel,

Qu'est ce qu'un Crontab ?

À ne pas confondre avec la commande at (une commande qui permet d'exécuter une tâche plus tard).

crontab est en fait une commande qui permet de lire et de modifier un fichier appelé la
« crontab ».

Ce fichier contient la liste des programmes que tu souhaites exécuter régulièrement, et à quelle heure tu souhaites qu'ils soient exécutés.

Je t'invite à lire la doc de la commande afin de voir les différentes options qu'elle te propose

man crontab


Il y a trois paramètres différents à connaître, pas plus :
* -e : modifier la crontab ;
* -l : afficher la crontab actuelle ;
* -r : supprimer ta crontab. Attention, la suppression est immédiate et sans confirmation !

Normalement, tu n'as pas encore créé de crontab. Tu noteras qu'il y a une crontab par utilisateur. C'est-à-dire que même root a son propre cron.

Comment éditer un cron ?
Si c'est pour le système, ce sera le crontab de root (donc en tant que su / sudo / sudo su).

Je sais, c'est débile, mais admettons que tu souhaites créer un fichier nommé temp.txt toutes les heures. Il te faut tout d'abords que tu exécutes la commande pour éditer un cron

Le fichier crontab se présente ainsi en faisant la commande :

crontab -e


# Example 
# .---------------- minute (0 - 59)
# |  .------------- heure (0 - 23)
# |  |  .---------- jour du mois (1 - 31)
# |  |  |  .------- mois (1 - 12) OU jan,feb,mar,apr ...
# |  |  |  |  .---- jour de la semaine (0 - 6) (Sunday=0 or 7) OU sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  *  la commande utilisateur


Ce qui va donner ainsi si tu souhaites créer ce fichier :
* */1 * * * touch chemin/vers/nouveau/fichier/temp.txt


Les différentes notations possibles
Pour chaque champ, on a le droit à différentes notations :
- 5 (un nombre) : exécuté lorsque le champ prend la valeur 5 ;
- * : exécuté tout le temps (toutes les valeurs sont bonnes) ;
- 3,5,10 : exécuté lorsque le champ prend la valeur 3, 5 ou 10. Ne pas mettre d'espace après la virgule ;
- 3-7 : exécuté pour les valeurs 3 à 7 ;
- */3 : exécuté tous les multiples de 3 (par exemple à 0 h, 3 h, 6 h, 9 h…).

Une fois que tu auras enregistré le fichier, tu devrais voir dans le terminal un message de succès.

Cependant, si ta commande est foireuse, tu ne peux pas voir d'où vient le problème. Tu vas devoir tracker et donc loguer
Ce qui va donner ceci :

* */1 * * * touch chemin/vers/nouveau/fichier/temp.txt >> chemin/vers/cron.log


Donc, pour ton problème, tu vas te créer un fichier php qui lui fera l'appel à l'API tous les 3-4 heures via un cron et génèrera un json ou autre format. Ensuite avec WP, tu appelles ton json. Une question : pourquoi avec wp, tu n'appelles pas directement cette API ?

ps :
1. Bien que formatux soit intéressant dans l'apprentissage d'un système Linux, la doc pour cron est succinctes (d'où mon détail dans ma réponse).
2. flux de redirection sortie
Modifié par niuxe (29 Aug 2024 - 10:48)
niuxe a écrit :
Une question : pourquoi avec wp, tu n'appelles pas directement cette API ?

Si j'ai bien lu Lionel, il s'agissait d'une optimisation liée à la performance.
Modérateur
Olivier C a écrit :

Si j'ai bien lu Lionel, il s'agissait d'une optimisation liée à la performance.


ça m'apprendra de lire en Z Smiley confused

Au passage, je dis UNE crontab et/ou UN crontab. J'avoue que chaque fois, je ne connais pas le genre. Puisque nous sommes dans l'époque du wokisme, disons que la commande est non binaire Smiley lol (pour du numérique, ça a du sens que ce soit du non binaire Smiley lol )
Hello,

Ce qu'indique Niuxe est à mon avis valable pour un hébergement dédié, sur lequel tu as accès au système via SSH et dans ce cas, c'est toi qui vas aller configurer tes crontabs Smiley smile

Si tu utilises un hébergement mutualisé pour ton WP genre OVH, Ionos, etc. il y a généralement une interface utilisateur très simpliste pour configurer des crons puisque tu n'as pas accès aux configs de la machine. On y trouve souvent les champs "URL" (quel URL ou endpoint appeler ? Le chemin vers ton script), ainsi que la récurrence (c'est parfois limité, pas très précis)
Il y a aussi d'autres params comme l'authentification (htaccess par exemple, si le endpoint est privé)

Je ne sais pas comment fonctionne le WP_CRON, mais je pense que le principe est toujours le même : tu écris un script (PHP par exemple) qui effectue l'action de mise à jour de tes données, puis c'est la cron qui iras l'exécuter périodiquement via son URL. Smiley smile
Modérateur
Loraga a écrit :

Si tu utilises un hébergement mutualisé pour ton WP genre OVH, Ionos, etc. il y a généralement une interface utilisateur très simpliste pour configurer des crons puisque tu n'as pas accès aux configs de la machine. On y trouve souvent les champs "URL" (quel URL ou endpoint appeler ?


oui bien sûr. Comment n'y ai-je pas pensé ? En effet, dans les hébergements, il y a souvent ce genre d'interface.

Loraga a écrit :

Je ne sais pas comment fonctionne le WP_CRON, mais je pense que le principe est toujours le même : tu écris un script (PHP par exemple) qui effectue l'action de mise à jour de tes données, puis c'est la cron qui iras l'exécuter périodiquement via son URL. Smiley smile


Le problème de wp_cron est contraignant et je pense que ça ne va pas aider Lionel. wp_cron va s'exécuter lorsque l'utilisateur visite la ou une page. Or, sur un système GNU/Linux, un cron s'exécute régulièrement. Il n'attend pas un événement/une requête.

Je viens de penser à une solution alternative (moins rapide à mettre en place et peut être moins efficace, je l'admets). Tu te crées un script JS qui va faire un Ajax toutes les 3 - 4 heures suivant l'heure du jour (au demeurant pas de setInterval Smiley cligne ). Ensuite, tu stockes ça dans le localstorage (les données de l'api + la date avec heure qui te fera un système de version). Le problème étant que tu te retrouves un peu dans le contexte du wp_cron. Il est peu probable que l'utilisateur reste 3-4 heures sur la même page (le correctif est dans ce cas, faire un setInterval de 10800 secondes).
Modifié par niuxe (29 Aug 2024 - 23:57)
niuxe a écrit :


ça m'apprendra de lire en Z Smiley confused

Au passage, je dis UNE crontab et/ou UN crontab. J'avoue que chaque fois, je ne connais pas le genre. Puisque nous sommes dans l'époque du wokisme, disons que la commande est non binaire Smiley lol (pour du numérique, ça a du sens que ce soit du non binaire Smiley lol )


Trop fort, la commande non binaire Smiley lol
Loraga a écrit :
Hello,

Ce qu'indique Niuxe est à mon avis valable pour un hébergement dédié, sur lequel tu as accès au système via SSH et dans ce cas, c'est toi qui vas aller configurer tes crontabs Smiley smile

Si tu utilises un hébergement mutualisé pour ton WP genre OVH, Ionos, etc. il y a généralement une interface utilisateur très simpliste pour configurer des crons puisque tu n'as pas accès aux configs de la machine. On y trouve souvent les champs "URL" (quel URL ou endpoint appeler ? Le chemin vers ton script), ainsi que la récurrence (c'est parfois limité, pas très précis)
Il y a aussi d'autres params comme l'authentification (htaccess par exemple, si le endpoint est privé)

Je ne sais pas comment fonctionne le WP_CRON, mais je pense que le principe est toujours le même : tu écris un script (PHP par exemple) qui effectue l'action de mise à jour de tes données, puis c'est la cron qui iras l'exécuter périodiquement via son URL. Smiley smile


Exact, mais chez certains hébergeurs, cron n'est pas activé, il faut leur demander.
Bonsoir,

Je fais un retour ici car je viens de tenter quelque chose d'approchant - avec toutefois une logique légèrement différente - et j'ai repensé à ce topic de Lionel. Alors moi je n'avais pas d'API (j'ai bien fait une demande mais personne au bout du fil) donc j'ai fait du sraping :

1. un premier utilisateur appelle une page de mon site qui déclenche un sraping permettant de requêter toutes les données nécessaires que je formate dans la foulée.
2. J'enregistre le tout dans un JSON, ainsi, TOUS les utilisateurs suivants bénéficierons des données directement disponibles dans le JSON.
3. Donc, si pas de JSON ou si JSON datant d'avant minuit du jour en cours, alors déclenchement du scraping, sinon recours aux données JSON.

Donc, la première différence est là : pas de tâche CRON, c'est le premier utilisateur de la journée qui lance le script. Ensuite, le JSON évite de devoir structurer une table à destination de données qui n'appartiennent pas à mon site : celles-ci peuvent évoluer au cours du temps, le fichier se doit de pouvoir être modulaire si besoin ; surtout pas une structure rigide comme une table SQL.

J'utilise uniquement Node.js, le script :
import { JSDOM } from 'jsdom'
import fs from 'fs/promises'
import path from 'path'
import { fileURLToPath } from 'url'

const jsonFilePath = path.join(path.dirname(fileURLToPath(import.meta.url)), '../data/eventsData.json')

function formatISODate(dateISO) {
  const date = new Date(dateISO)
  const formattedDate = new Intl.DateTimeFormat('fr-FR', {
    weekday: 'long',
    day: 'numeric',
    month: 'long',
    year: 'numeric',
    hour: 'numeric',
    minute: 'numeric',
  }).format(date)
  return formattedDate.charAt(0).toUpperCase() + formattedDate.slice(1)
}

function formatEventDate(date) {
  return date
    .replace(/ -/, ',')
    .replace(/(09:00)/, '<span style="color:var(--color5)">$1</span>')
    .replace(/ -/, ',')
    .replace(/(10:00)/, '<span style="color:var(--color2)">$1</span>')
}

function formatLocation(location) {
  return location
    .toLowerCase()
    .split(' ')
    .map(word => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
    .join('-')
}

function processEvents(events) {
  return events.map(event => ({
    formattedDate: formatEventDate(formatISODate(event.dateISO)),
    formattedLocation: formatLocation(event.location),
  }))
}

async function scrapeWebsite(url) {
  try {
    const response = await fetch(url)
    if (!response.ok) {
      throw new Error(`Erreur HTTP : ${response.status}`)
    }
    const html = await response.text()
    const dom = new JSDOM(html)
    const document = dom.window.document
    const rows = document.querySelectorAll('article')

    const events = []
    rows.forEach(row => {
      const dateISO = row.querySelector('meta[itemprop="startDate"]').getAttribute('content')
      const location = row.querySelector('h4 [itemprop="addressLocality"]')?.textContent.trim()

      if (dateISO && location) {
        events.push({ dateISO, location })
      }
    })

    return events
  } catch (error) {
    console.error('Erreur lors du scraping de', url, ':', error.message)
    return []
  }
}

async function scrapeMultiplePages() {
  const urls = ['https://messes.info/horaires/CODE1', 'https://messes.info/horaires/CODE2']
  const results = await Promise.all(urls.map(url => scrapeWebsite(url)))
  const aggregatedEvents = results.flat()
  const sortedEvents = aggregatedEvents.sort((a, b) => new Date(a.dateISO) - new Date(b.dateISO))
  return processEvents(sortedEvents)
}

async function readJsonData() {
  try {
    const data = await fs.readFile(jsonFilePath, 'utf8')
    return JSON.parse(data)
  } catch (error) {
    return null
  }
}

async function writeJsonData(data) {
  const jsonData = JSON.stringify(data, null, 2)
  await fs.writeFile(jsonFilePath, jsonData, 'utf8')
}

async function getStoredEvents() {
  const data = await readJsonData()
  if (data) {
    const lastUpdated = new Date(data.lastUpdated)
    const now = new Date()
    const isOutdated = now.setHours(0, 0, 0, 0) > lastUpdated.setHours(0, 0, 0, 0)

    if (!isOutdated) {
      return data.events
    }
  }
  return null
}

async function getPlayground(req, res) {
  let data = {}
  data.url = req.url
  data.name = 'Playground <span>.&nbsp;Page de test</span>'
  data.title = 'Playground, page de test'
  data.description = 'Page de test.'

  let events = await getStoredEvents()

  if (!events) {
    events = await scrapeMultiplePages()
    await writeJsonData({ lastUpdated: new Date(), events })
  }

  data.content = events
  return res.view('playground', { data })
}

export { getPlayground }

Tout ça pour obtenir les horaires des messes dans mon secteur car je suis à cheval sur 3 paroisses et que j'en avais marre de devoir consulter tous les sites... Smiley cligne
Modifié par Olivier C (07 Sep 2024 - 06:39)