{% extends 'sam/layout.html.twig' %} {% block title %}SAM — Paramètres{% endblock %} {% block sam_body %}
Base URL : {{ app.request.schemeAndHttpHost }}/api/moodlev5
Toutes les routes requièrent une clé API transmise via header HTTP.
X-API-Key: bubul_sk_xxxxxxxxxxxx
# Alternative — Authorization header
Authorization: Bearer bubul_sk_xxxxxxxxxxxx
Informations sur l'utilisateur authentifié et statut Moodle.
{# GET /me #}/me
Informations du compte connecté
Aucun paramètre requis.
{
"success": true,
"data": {
"user": {
"id": 42,
"email": "vous@entreprise.com",
"first_name": "Prénom",
"last_name": "Nom",
"roles": ["ROLE_USER"],
"company": {
"uuid": "550e8400-e29b-41d4...",
"name": "Mon Entreprise",
"has_moodle_config": true,
"has_active_contract": true
}
}
}
}
/status
Statut de connexion Moodle
Admins : ajouter ?company={uuid}
{
"success": true,
"data": {
"status": {
"configured": true,
"connected": true,
"moodle_url": "https://moodle.entreprise.com",
"site_name": "Ma Plateforme Moodle",
"site_version": "4.3.0"
}
}
}
Historique des connexions des apprenants par jour/mois.
{# POST /connections/refresh #}/connections/refresh
Synchroniser + retourner 12 mois
Aucun body requis. Lance une sync depuis la DB locale, dispatche un job async pour les refreshs futurs.
{
"success": true,
"inserted": 42,
"data": [
{ "month": "2026-01", "label": "Janv.", "count": 125 },
...
],
"total": 348,
"months": 12
}
/connections/company
Connexions mensuelles de l'entreprise
| Paramètre | Type | Description | Requis |
|---|---|---|---|
| period | integer | Période en mois : 3, 6 ou 12 | Non (défaut: 12) |
| company | uuid | UUID entreprise (admins uniquement) | Non |
{
"data": [{ "month": "2026-01", "label": "Janv.", "count": 125 }, ...],
"total": 348,
"months": 12,
"company": "Mon Entreprise"
}
/connections/user/{id}
Historique d'un apprenant
| Paramètre | Type | Description | Requis |
|---|---|---|---|
| id | integer | ID interne de l'apprenant | Oui (path) |
| days | integer | Nombre de jours (max 365) | Non (défaut: 90) |
{
"data": [{ "day": "2026-01-15" }, ...],
"total": 28,
"days": 90,
"user": "Jean Dupont"
}
Statistiques d'engagement des apprenants (statut actif/inactif).
{# POST /engagement/refresh #}/engagement/refresh
Synchroniser + retourner les stats
Synchronise les utilisateurs depuis Moodle, puis retourne les stats d'engagement.
{
"success": true,
"stats": {
"total": 150,
"active": 95,
"inactive2": 30,
"inactive10": 8,
"neverConnected": 2
}
}
/engagement/company
Stats engagement entreprise
Mêmes stats que refresh, sans déclencher de sync. Admins : ?company={uuid}
{
"success": true,
"stats": {
"total": 150,
"active": 95,
"inactive2": 30,
"inactive10": 8,
"neverConnected": 2
},
"company": "Mon Entreprise"
}
/engagement/user/{id}
Statut d'engagement d'un apprenant
{
"success": true,
"user": "Jean Dupont",
"lastAccessAt": "2026-03-06",
"daysSince": 5,
"status": "active"
// active | inactive2 | inactive10 | neverConnected
}
Complétions et soumissions récentes des apprenants.
/submissions
Liste paginée des soumissions
| Paramètre | Type | Description | Requis |
|---|---|---|---|
| page | integer | Numéro de page | Non (défaut: 1) |
| limit | integer | Résultats par page (max 50) | Non (défaut: 10) |
| user | string | Filtre par nom d'apprenant | Non |
| type | string | Type d'activité : quiz, assign... | Non |
| days | integer | Période en jours (max 365) | Non (défaut: 30) |
{
"data": [{
"userId": 42,
"userName": "Jean Dupont",
"action": "completed",
"activityName": "Quiz final",
"type": "quiz",
"completedAt": "2026-03-10T14:30:00+00:00",
"timeAgo": "il y a 1 jour"
}],
"total": 143
}
CRUD complet sur les utilisateurs Moodle. Les lectures se font depuis la DB locale (rapide), les écritures synchronisent vers Moodle. Le paramètre {id} est le moodle_id de l'apprenant (champ moodle_id retourné dans la liste).
/users
Liste paginée
| Paramètre | Type | Description | Requis |
|---|---|---|---|
| search | string | Recherche par email ou nom | Non |
| page | integer | Numéro de page | Non (défaut: 1) |
| per_page | integer | Résultats par page (max 100) | Non (défaut: 20) |
{
"success": true,
"data": {
"items": [{
"moodle_id": 42,
"username": "jdupont",
"email": "jean@entreprise.com",
"firstname": "Jean", "lastname": "Dupont",
"fullname": "Jean Dupont",
"suspended": false,
"syncedAt": "2026-03-10T08:00:00+00:00",
"createdAt": "2025-01-15T10:00:00+00:00",
"updatedAt": "2026-03-10T08:00:00+00:00"
}],
"pagination": {
"total": 150, "page": 1,
"per_page": 20, "total_pages": 8, "has_more": true
}
}
}
/users/{moodleId}
Détail d'un apprenant
Retourne l'objet user complet.
/users
Créer un apprenant
| Champ JSON | Type | Description | Requis |
|---|---|---|---|
| username | string | Identifiant Moodle unique | Oui |
| string | Adresse email | Oui | |
| firstName | string | Prénom (aussi accepté : first_name) | Oui |
| lastName | string | Nom (aussi accepté : last_name) | Oui |
| password | string | Mot de passe (généré si absent) | Non |
| auth | string | Méthode auth Moodle | Non (défaut: manual) |
201 Created avec l'objet user./users/{moodleId}
Mettre à jour un apprenant
Champs acceptés (tous optionnels) : email, firstName, lastName, password, suspended (boolean).
/users/{moodleId}
Supprimer (désactiver sur Moodle)
Réponse : 200 OK avec "message": "Utilisateur supprimé avec succès".
/users/{moodleId}/toggle
Activer / Désactiver
Bascule l'état suspended de l'utilisateur. Retourne l'objet user mis à jour.
/users/{moodleId}/courses
Cours de l'apprenant (temps réel)
Données récupérées en direct depuis Moodle. Retourne la liste des cours auxquels l'utilisateur est inscrit.
/users/sync
Synchroniser depuis Moodle
{
"success": true,
"data": {
"stats": {
"total": 150,
"created": 5,
"updated": 20,
"deleted": 0
}
},
"message": "Synchronisation terminée : 150 utilisateurs"
}
Accès aux cours Moodle et à leurs contenus (sections, activités, cohortes).
{% for endpoint in [ { method: 'GET', path: '/courses', desc: 'Liste paginée (params: search, page, per_page)' }, { method: 'GET', path: '/courses/{id}', desc: 'Détail d\'un cours' }, { method: 'GET', path: '/courses/{id}/users', desc: 'Apprenants inscrits avec progression (paginé)' }, { method: 'GET', path: '/courses/{id}/detail', desc: 'Détail complet : sections, activités, cohortes, stats' }, { method: 'GET', path: '/courses/{id}/sections', desc: 'Sections du cours' }, { method: 'GET', path: '/courses/{id}/cohorts', desc: 'Cohortes inscrites au cours (cache local)' } ] %}{{ endpoint.path|replace({'{id}': '{id}'})|raw }}
{{ endpoint.desc }}
Inscrire ou désinscrire manuellement un apprenant à un cours. Rôle ROLE_COMPANY_MANAGER requis.
/courses/{courseId}/enrollments
Inscrire un apprenant
| Champ JSON | Type | Description | Requis |
|---|---|---|---|
| userId | integer | ID Moodle de l'apprenant (aussi : user_id) | Oui |
| roleId | integer | ID rôle Moodle (aussi : role_id) | Non (défaut: 5 = Étudiant) |
Réponse : 201 Created.
/courses/{courseId}/enrollments/{userId}
Désinscrire un apprenant
/courses/{courseId}/enrollments/search
Rechercher des apprenants inscriptibles
Paramètres : q (string, min 2 chars, requis), limit (max 50, défaut 20). Retourne les utilisateurs non encore inscrits.
Gestion complète des cohortes Moodle et de leurs membres.
{% for endpoint in [ { method: 'GET', path: '/cohorts', desc: 'Liste paginée (params: page, per_page)' }, { method: 'POST', path: '/cohorts', desc: 'Créer une cohorte (body: name*, idNumber, description)' }, { method: 'GET', path: '/cohorts/{id}', desc: 'Détail d\'une cohorte' }, { method: 'PUT', path: '/cohorts/{id}', desc: 'Mettre à jour (body: name, idNumber, description)' }, { method: 'DELETE', path: '/cohorts/{id}', desc: 'Supprimer une cohorte' }, { method: 'GET', path: '/cohorts/{id}/members', desc: 'Liste des membres (paginé)' }, { method: 'POST', path: '/cohorts/{id}/members', desc: 'Ajouter des membres (body: userIds[])' }, { method: 'DELETE', path: '/cohorts/{id}/members', desc: 'Retirer des membres (body: userIds[])' }, { method: 'GET', path: '/cohorts/{id}/courses', desc: 'Cours accessibles via la cohorte (cache local)' } ] %}{{ endpoint.path|replace({'{id}': '{id}'})|raw }}
{{ endpoint.desc }}
// Request body
{ "userIds": [42, 43, 99] }
// Response — 200 OK
{
"added": [42, 43],
"already_members": [],
"not_found": [99]
}
// DELETE /cohorts/{id}/members — réponse 200 OK
{
"removed": [42],
"not_members": [43],
"not_found": []
}
// Request body — idNumber et id_number sont tous les deux acceptés
{
"name": "Promo 2026 - Groupe A",
"idNumber": "PROMO-2026-A",
"description": "Groupe A de la promotion 2026"
}
// Response — 201 Created
{
"success": true,
"data": {
"cohort": {
"id": 7, "moodle_id": 12,
"name": "Promo 2026 - Groupe A",
"idnumber": "PROMO-2026-A",
"description": "Groupe A de la promotion 2026",
"members_count": 0
}
},
"message": "Cohorte créée avec succès"
}
Données de progression lues depuis la DB locale (SamMoodleCompletion + SamQuizAttempt). Synchroniser au préalable.
{% for endpoint in [ { method: 'GET', path: '/progress/users/{userId}', desc: 'Progression globale d\'un apprenant (tous ses cours)' }, { method: 'GET', path: '/progress/users/{userId}/courses/{courseId}', desc: 'Progression détaillée dans un cours' }, { method: 'GET', path: '/progress/courses/{courseId}', desc: 'Progression de tous les apprenants d\'un cours (paginé)' }, { method: 'GET', path: '/progress/courses/{courseId}/sections/{sectionId}', desc: 'Progression d\'une section (param optionnel: user_id)' }, { method: 'GET', path: '/progress/activities/{activityId}', desc: 'Métadonnées d\'une activité' }, { method: 'GET', path: '/progress/quizzes/{quizId}', desc: 'Tentatives d\'un quiz (param optionnel: user_id)' }, { method: 'GET', path: '/progress/quizzes/{quizId}/attempts/{attemptId}', desc: 'Détail d\'une tentative de quiz' } ] %}{{ endpoint.path|replace({'{userId}': '{userId}', '{courseId}': '{courseId}', '{sectionId}': '{sectionId}', '{activityId}': '{activityId}', '{quizId}': '{quizId}', '{attemptId}': '{attemptId}'})|raw }}
{{ endpoint.desc }}
{
"success": true,
"data": {
"user_id": 42,
"course_id": 7,
"completion_rate": 68.5,
"completed_activities": 11,
"total_activities": 16,
"last_activity_at": "2026-03-09T10:15:00+00:00"
}
}
Accès unitaire à une activité ou section. Le paramètre course_id est obligatoire en query string (limitation API Moodle — une activité n'est identifiable qu'avec son cours).
{{ endpoint.path|replace({'{id}': '{id}', '{cid}': '{courseId}'})|raw }}
{{ endpoint.desc }}
{
"success": true,
"data": {
"activity": { "id": 18, "name": "Quiz final", "type": "quiz" },
"completions": [{
"user_id": 42, "fullname": "Jean Dupont",
"state": 1, "completed_at": "2026-03-10T14:30:00+00:00"
}],
"stats": {
"total_users": 30, "completed": 24,
"not_completed": 6,
"completion_rate": 80.0
}
}
}
Toutes les erreurs retournent le même format.
{{ code }}
{{ desc }}
{
"success": false,
"error": "Ressource introuvable",
"code": 404,
"details": {}
}
Les 50 derniers appels enregistrés sur les routes SAM
| Date | Méthode | Endpoint | Utilisateur | Statut | |
|---|---|---|---|---|---|
| {{ log.createdAt|format_datetime('short', 'short', locale='fr') }} | {% include 'sam/_api_method_badge.html.twig' with { method: log.method } only %} | {{ log.path }} | {{ log.fullName ?? '—' }} | {{ log.email ?? '—' }} | {% include '_atoms/badge/badge.html.twig' with { label: log.statusCode, variant: log.statusCode < 300 ? 'success' : (log.statusCode < 500 ? 'warning' : 'error'), size: 'sm' } only %} |