connecteur” prêt à l’emploi pour interroger Odoo eLearning et récupérer slide.id,

Voici un mini “connecteur” prêt à l’emploi pour interroger Odoo eLearning et récupérer slide.id, channel_id, website_url sur le modèle slide.slide — en XML-RPC (Python) et JSON-RPC (Node.js).

Python (XML-RPC)

# pip install requests xmlrpc-client==... (intégré en stdlib: xmlrpc.client) import xmlrpc.client ODOO_URL = "https://votre-odoo.tld" DB = "votre_bdd" USER = "email@exemple.com" PASSWORD = "votre_mot_de_passe" # 1) Authentification common = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/common") uid = common.authenticate(DB, USER, PASSWORD, {}) if not uid: raise RuntimeError("Échec d'authentification") # 2) Proxy objets models = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/object") # 3) Recherche + lecture # Domain d’exemple: slides publiés (is_published=True). Adaptez selon vos besoins. domain = [["is_published", "=", True]] fields = ["id", "channel_id", "website_url"] # vous pouvez ajouter "name", "slide_type", etc. slide_ids = models.execute_kw(DB, uid, PASSWORD, "slide.slide", "search", [domain], {"limit": 1000} # pagination si besoin ) slides = models.execute_kw(DB, uid, PASSWORD, "slide.slide", "read", [slide_ids], {"fields": fields} ) # 4) Normalisation du Many2one (channel_id = [id, nom]) resultats = [ { "id": s["id"], "channel_id": s["channel_id"][0] if s.get("channel_id") else None, "channel_name": s["channel_id"][1] if s.get("channel_id") else None, "website_url": s.get("website_url"), } for s in slides ] for r in resultats: print(r)

Node.js (JSON-RPC)

// npm i axios tough-cookie axios-cookiejar-support tough-cookie const axios = require("axios").default; const { wrapper } = require("axios-cookiejar-support"); const { CookieJar } = require("tough-cookie"); const ODOO_URL = "https://votre-odoo.tld"; const DB = "votre_bdd"; const USER = "email@exemple.com"; const PASSWORD = "votre_mot_de_passe"; const jar = new CookieJar(); const client = wrapper(axios.create({ baseURL: ODOO_URL, withCredentials: true, jar })); async function jsonrpc(route, params) { const payload = { jsonrpc: "2.0", method: "call", params, id: Math.floor(Math.random()*1e9) }; const { data } = await client.post(route, payload, { headers: { "Content-Type": "application/json" } }); if (data.error) throw new Error(JSON.stringify(data.error)); return data.result; } (async () => { // 1) Auth (web/session/authenticate) await jsonrpc("/web/session/authenticate", { db: DB, login: USER, password: PASSWORD }); // 2) search_read sur slide.slide const result = await jsonrpc("/web/dataset/call_kw", { model: "slide.slide", method: "search_read", args: [], kwargs: { domain: [["is_published","=",true]], // ajustez votre domain fields: ["id","channel_id","website_url"], limit: 1000 } }); // channel_id = [id, nom] (many2one) const rows = result.map(s => ({ id: s.id, channel_id: Array.isArray(s.channel_id) ? s.channel_id[0] : null, channel_name: Array.isArray(s.channel_id) ? s.channel_id[1] : null, website_url: s.website_url })); console.log(rows); })();

Notes utiles

  • Modèle : slide.slide (eLearning).
  • Champs : id, channel_id (Many2one vers slide.channel), website_url (disponible si le site web est installé et le mixin website actif).
  • Filtrage : adaptez le domain (ex. par canal : ["channel_id","=",123] ; par type : ["slide_type","=","video"]).
  • Pagination : utilisez limit/offset ou récupérez les IDs puis paginez en read.

ANNEXES

voici les versions avec filtre par channel_id ou channel_slug prêtes à copier-coller.

Python (XML-RPC) — filtre par channel

import xmlrpc.client ODOO_URL = "https://votre-odoo.tld" DB = "votre_bdd" USER = "email@exemple.com" PASSWORD = "votre_mot_de_passe" common = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/common") uid = common.authenticate(DB, USER, PASSWORD, {}) models = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/object") def get_channel_id_from_slug(slug: str): ids = models.execute_kw(DB, uid, PASSWORD, "slide.channel", "search", [[["slug","=",slug]]], {"limit": 1}) return ids[0] if ids else None def get_slides(channel_id: int | None = None, channel_slug: str | None = None, limit: int = 1000): if channel_slug and not channel_id: channel_id = get_channel_id_from_slug(channel_slug) if not channel_id: return [] domain = [["is_published","=",True]] if channel_id: domain.append(["channel_id","=",channel_id]) slide_ids = models.execute_kw(DB, uid, PASSWORD, "slide.slide", "search", [domain], {"limit": limit}) slides = models.execute_kw(DB, uid, PASSWORD, "slide.slide", "read", [slide_ids], {"fields": ["id","channel_id","website_url","name"]}) return [ { "id": s["id"], "name": s.get("name"), "channel_id": s["channel_id"][0] if s.get("channel_id") else None, "channel_name": s["channel_id"][1] if s.get("channel_id") else None, "website_url": s.get("website_url"), } for s in slides ] # Exemples d’usage print(get_slides(channel_id=123)) print(get_slides(channel_slug="mon-canal-elearning"))

Node.js (JSON-RPC) — filtre par channel

const axios = require("axios").default; const { wrapper } = require("axios-cookiejar-support"); const { CookieJar } = require("tough-cookie"); const ODOO_URL = "https://votre-odoo.tld"; const DB = "votre_bdd"; const USER = "email@exemple.com"; const PASSWORD = "votre_mot_de_passe"; const client = wrapper(axios.create({ baseURL: ODOO_URL, withCredentials: true, jar: new CookieJar() })); async function rpc(route, params) { const payload = { jsonrpc: "2.0", method: "call", params, id: Date.now() }; const { data } = await client.post(route, payload, { headers: {"Content-Type":"application/json"} }); if (data.error) throw new Error(JSON.stringify(data.error)); return data.result; } async function auth() { await rpc("/web/session/authenticate", { db: DB, login: USER, password: PASSWORD }); } async function getChannelIdFromSlug(slug) { const ids = await rpc("/web/dataset/call_kw", { model: "slide.channel", method: "search", args: [[["slug","=",slug]]], kwargs: { limit: 1 } }); return ids.length ? ids[0] : null; } async function getSlides({ channel_id=null, channel_slug=null, limit=1000 } = {}) { if (channel_slug && !channel_id) { channel_id = await getChannelIdFromSlug(channel_slug); if (!channel_id) return []; } const domain = [["is_published","=",true]]; if (channel_id) domain.push(["channel_id","=",channel_id]); const slides = await rpc("/web/dataset/call_kw", { model: "slide.slide", method: "search_read", args: [], kwargs: { domain, fields: ["id","name","channel_id","website_url"], limit } }); return slides.map(s => ({ id: s.id, name: s.name, channel_id: Array.isArray(s.channel_id) ? s.channel_id[0] : null, channel_name: Array.isArray(s.channel_id) ? s.channel_id[1] : null, website_url: s.website_url })); } // Exemple d’usage (async () => { await auth(); console.log(await getSlides({ channel_id: 123 })); console.log(await getSlides({ channel_slug: "mon-canal-elearning" })); })();

Remarques rapides

  • Le champ slug est sur slide.channel. Si vous utilisez des URL de type /slides/<id>-<slug>, le slug ici correspond bien au segment lisible.
  • Pour filtrer par plusieurs canaux : remplacez ["channel_id","=",id] par ["channel_id","in",[1,2,3]].