IWEY API

IWEY API — Structure académique (REST)

Spécification des endpoints programmes, niveaux, classes, cohortes et inscriptions cours

API — Structure académique & inscriptions aux cours

Spécification REST pour la gestion des programmes d’études, niveaux, classes, cohortes et inscriptions aux cours (individuelles ou groupées).

Référence conceptuelle : /academic-concepts
DDL d’extension : database/03_academic_structure_api.sql


1. Module NestJS

src/modules/academic-structure/
├── academic-structure.module.ts
├── controllers/
│   ├── programs.controller.ts
│   ├── program-curriculum.controller.ts
│   ├── levels.controller.ts
│   ├── academic-years.controller.ts
│   ├── school-classes.controller.ts
│   ├── cohorts.controller.ts
│   └── departments.controller.ts          # lecture seule (listes déroulantes)
├── services/
│   ├── program.service.ts
│   ├── program-curriculum.service.ts
│   ├── level.service.ts
│   ├── academic-year.service.ts
│   ├── school-class.service.ts
│   ├── cohort.service.ts
│   └── course-enrollment.service.ts       # port vers pedagogy + email
├── interfaces/                            # ports repository
├── repositories/                        # TypeORM (cible) puis in-memory (phase 1)
└── dto/

Extension pedagogy : CourseEnrollmentsController sous /api/courses/:courseId/enrollments (service partagé ou import du port CourseEnrollmentService).

Guards globaux : JwtAuthGuard, PermissionsGuard — deny-by-default.


2. Modèle de données (résumé)

department
├── program ──► program_level ──► semester ──► teaching_unit ──► subject
│                  (niveaux du cursus)     (périodes)      (UE)         (matières)
├── cohort ──► cohort_member ──► student
└── course (pedagogy) ──► course_participation ──► student

school_class ──► class_student ──► student
     │
     └── program + level + academic_year
Table Rôle
program Formation / cursus (diplôme, filière, durée)
level Catalogue global LMD (Licence 1, Master 1…)
program_level Niveau d’un programme (ordre, ECTS)
semester Période académique d’un niveau
teaching_unit UE
subject Matière (lien optionnel course_id → cours LMS)
school_class Classe (programme + niveau + année + capacité)
class_student Affectation étudiant ↔ classe
cohort Cohorte (programme + année)
cohort_member Affectation étudiant ↔ cohorte
course_participation Inscription étudiant ↔ cours
course_enrollment_batch Journal d’une inscription groupée

3. Conventions API

Règle Valeur
Préfixe /api
Auth Bearer JWT (access-token)
Pagination ?page=1&limit=20{ items, meta: { page, limit, total } }
Tri ?sort=name&order=asc
Recherche ?q= (nom, code)
IDs UUID v4
Erreurs I18nHttpException + clés academic.*, enrollment.*
Dates ISO 8601 (2024-09-01 ou 2024-09-01T00:00:00.000Z)

4. Programmes d’études (/api/programs)

Permission lecture : cursus.view | program.manage
Permission écriture : program.manage | cursus.manage (curriculum)

4.1 Lister les programmes

GET /api/programs

Query : departmentId, status (draft|active|archived), q, page, limit

Réponse 200 :

{
  "items": [
    {
      "id": "uuid",
      "code": "MCI",
      "name": "Master Commerce international",
      "specialty": "Commerce international",
      "degreeType": "master",
      "durationYears": 2,
      "totalEcts": 120,
      "status": "active",
      "levelCount": 2,
      "department": { "id": "uuid", "name": "Commerce" }
    }
  ],
  "meta": { "page": 1, "limit": 20, "total": 3 }
}

4.2 Détail programme

GET /api/programs/:programId

Inclut : général, diplôme, compteurs (niveaux, semestres, ECTS).

4.3 Créer un programme (brouillon)

POST /api/programs

Body :

{
  "departmentId": "uuid",
  "code": "MCI",
  "name": "Master Commerce international",
  "specialty": "Commerce international",
  "description": "Parcours M1–M2…",
  "degreeType": "master",
  "durationYears": 2,
  "totalEcts": 120,
  "diplomaTitle": "Master Commerce international"
}

Réponse 201 : objet programme complet (status: "draft").

4.4 Mettre à jour le général

PATCH /api/programs/:programId/general

Body partiel : name, description, specialty, degreeType, durationYears, totalEcts.

4.5 Diplôme

PATCH /api/programs/:programId/diploma

Body : { "diplomaTitle": "Master Commerce international" }

4.6 Statut

PATCH /api/programs/:programId/status

Body : { "status": "active" | "archived" }
Règle : active exige au moins 1 program_level.

4.7 Supprimer

DELETE /api/programs/:programId

409 si classes/cohortes actives liées.


5. Cursus — niveaux, semestres, UE (/api/programs/:programId/curriculum)

Permission : cursus.manage

5.1 Lire le cursus (arbre complet)

GET /api/programs/:programId/curriculum

Réponse :

{
  "programId": "uuid",
  "levels": [
    {
      "id": "uuid",
      "levelId": "uuid",
      "label": "Master 1 (M1)",
      "sortOrder": 1,
      "ects": 60,
      "semesterCount": 2,
      "semesters": [
        {
          "id": "uuid",
          "number": 1,
          "startDate": "2024-09-01",
          "endDate": "2025-01-31",
          "ects": 30,
          "teachingUnits": [
            {
              "id": "uuid",
              "code": "UE-FCI",
              "name": "Fondamentaux du commerce international",
              "ects": 30,
              "subjectCount": 2,
              "subjects": [
                { "id": "uuid", "code": "MAT-101", "name": "Introduction", "hours": 24 }
              ]
            }
          ]
        }
      ]
    }
  ]
}

5.2 Remplacer le cursus (idempotent)

PUT /api/programs/:programId/curriculum

Body : même structure sans IDs obligatoires (création) ; IDs présents = mise à jour ; niveaux absents = suppression (si pas de dépendances).

Validations :

  • sortOrder unique par programme ;
  • somme ECTS semestres ≤ ECTS niveau (avertissement ou erreur configurable) ;
  • dates semestre cohérentes.

Réponse 200 : arbre complet après persistance.


6. Niveaux d’étude — catalogue (/api/levels)

Permission lecture : cursus.view
Permission écriture : level.manage

6.1 Lister

GET /api/levels?type=LMD&active=true&q=

Réponse :

{
  "items": [
    {
      "id": "uuid",
      "name": "Licence 1",
      "type": "LMD",
      "sortOrder": 1,
      "isActive": true
    }
  ]
}

6.2 Créer / modifier / désactiver

POST   /api/levels
PATCH  /api/levels/:levelId
PATCH  /api/levels/:levelId/status   → { "isActive": false }

Body création : { "name", "type": "LMD"|"training", "sortOrder" }


7. Années académiques (/api/academic-years)

Permission lecture : cursus.view
Permission écriture : academic_year.manage

GET    /api/academic-years?status=active
POST   /api/academic-years
PATCH  /api/academic-years/:yearId

Body : { "label": "2024-2025", "startDate", "endDate", "status": "planned"|"active"|"archived" }


8. Classes (/api/school-classes)

Permission lecture : cursus.view
Permission écriture : program.manage

8.1 Lister (écran Classes)

GET /api/school-classes?q=&levelId=&academicYearId=&programId=&status=

Réponse item :

{
  "id": "uuid",
  "name": "L1 Informatique — Groupe A",
  "specialty": "Informatique",
  "program": { "id": "uuid", "name": "Licence Informatique" },
  "level": { "id": "uuid", "name": "Licence 1" },
  "academicYear": { "id": "uuid", "label": "2024-2025" },
  "studentCount": 35,
  "maxCapacity": 40,
  "status": "active"
}

8.2 CRUD classe

POST   /api/school-classes
GET    /api/school-classes/:classId
PATCH  /api/school-classes/:classId
DELETE /api/school-classes/:classId

Body création (modal UI) :

{
  "name": "L1 Informatique — Groupe A",
  "programId": "uuid",
  "levelId": "uuid",
  "academicYearId": "uuid",
  "maxCapacity": 40,
  "status": "active"
}

Règles :

  • studentCountmaxCapacity à l’ajout d’étudiants ;
  • un étudiant = une classe active (class_student.end_date IS NULL).

8.3 Membres de la classe

GET    /api/school-classes/:classId/students
POST   /api/school-classes/:classId/students        → { "studentIds": ["uuid"] }
DELETE /api/school-classes/:classId/students/:studentId

POST : 409 si capacité dépassée ; 404 si étudiant inconnu.


9. Cohortes (/api/cohorts)

Permission lecture : cursus.view
Permission écriture : cohort.manage

9.1 Lister (écran Cohortes)

GET /api/cohorts?q=&programId=&academicYearId=&status=active|archived|all

Réponse item :

{
  "id": "uuid",
  "name": "Cohorte L1 Informatique",
  "description": "Première année licence informatique",
  "program": { "id": "uuid", "name": "Licence Informatique" },
  "academicYear": { "id": "uuid", "label": "2024-2025" },
  "studentCount": 42,
  "status": "active",
  "createdAt": "2025-09-01T08:00:00.000Z"
}

9.2 CRUD cohorte

POST   /api/cohorts
GET    /api/cohorts/:cohortId
PATCH  /api/cohorts/:cohortId
DELETE /api/cohorts/:cohortId

Body création (modal UI) :

{
  "name": "Cohorte L1 Informatique",
  "description": "Première année licence informatique",
  "programId": "uuid",
  "academicYearId": "uuid",
  "status": "active"
}

9.3 Membres de la cohorte

GET    /api/cohorts/:cohortId/students
POST   /api/cohorts/:cohortId/students     → { "studentIds": ["uuid"] }
DELETE /api/cohorts/:cohortId/students/:studentId

10. Inscriptions aux cours

Les cours sont offerts par le département (pedagogy.course.department_id).
L’inscription ne lie pas le cours à un programme : elle lie des étudiants au cours.

Permission : student.manage + course.participants.view (lecture)

10.1 Lister les inscrits

GET /api/courses/:courseId/enrollments?q=&source=individual|cohort|class

Réponse :

{
  "items": [
    {
      "studentId": "uuid",
      "student": { "firstName": "Jean", "lastName": "Kouassi", "email": "…" },
      "role": "student",
      "enrolledAt": "2025-09-15",
      "enrollmentSource": "cohort",
      "sourceCohortId": "uuid",
      "notificationSentAt": "2025-09-15T10:00:00.000Z"
    }
  ]
}

10.2 Inscription individuelle

POST /api/courses/:courseId/enrollments/individual

Body :

{
  "studentIds": ["uuid-1", "uuid-2"],
  "notifyStudents": true
}

Réponse 201 :

{
  "batchId": "uuid",
  "enrollmentSource": "individual",
  "requestedCount": 2,
  "enrolledCount": 2,
  "skippedCount": 0,
  "skipped": [],
  "notificationsQueued": 2
}

Comportement :

  • idempotent : étudiant déjà inscrit → skipped avec raison already_enrolled ;
  • email si notifyStudents: true (SMTP ou log console).

10.3 Inscription par cohorte

POST /api/courses/:courseId/enrollments/by-cohort

Body :

{
  "cohortId": "uuid",
  "notifyStudents": true
}

Résout tous les cohort_member actifs (left_at IS NULL). Même réponse batch que §10.2.

10.4 Inscription par classe

POST /api/courses/:courseId/enrollments/by-class

Body :

{
  "schoolClassId": "uuid",
  "notifyStudents": true
}

Résout tous les class_student actifs (end_date IS NULL).

10.5 Désinscription

DELETE /api/courses/:courseId/enrollments/:studentId

Réponse 204. Ne supprime pas l’appartenance classe/cohorte.

10.6 Email de notification

Template (i18n enrollment.course_welcome) — contenu :

Champ Source
Nom du cours course.title
Code course.code
Département department.name
Enseignant profil lead_teacher_id
Lien {FRONTEND_URL}/courses/{courseId}

Service : CourseEnrollmentService → port EmailSender (module communication existant).


11. Départements (/api/departments)

Permission lecture : cursus.view
Permission écriture : department.manage

GET    /api/departments?siteId=
GET    /api/departments/:departmentId
POST   /api/departments
PATCH  /api/departments/:departmentId
DELETE /api/departments/:departmentId

Body création :

{
  "siteId": "uuid",
  "name": "Informatique & Numérique",
  "headId": "uuid"
}

DELETE409 si des programmes sont encore liés.


12. Matrice permissions

Endpoint Permission(s)
GET /programs, /levels, /academic-years, /school-classes, /cohorts cursus.view
POST/PATCH/DELETE /departments department.manage
POST/PATCH/DELETE /programs program.manage
PUT /programs/:id/curriculum cursus.manage
POST/PATCH /levels level.manage
POST/PATCH /academic-years academic_year.manage
POST/PATCH/DELETE /school-classes program.manage
POST/PATCH/DELETE /cohorts, membres cohorte cohort.manage
Membres classe program.manage ou student.manage
Inscriptions cours student.manage
GET …/enrollments course.participants.view

13. Clés i18n à ajouter

Clé Usage
academic.program_not_found 404 programme
academic.level_not_found 404 niveau
academic.class_not_found 404 classe
academic.class_capacity_exceeded 409 capacité
academic.cohort_not_found 404 cohorte
academic.program_has_dependencies 409 suppression
enrollment.already_enrolled skip batch
enrollment.course_not_found 404 cours
enrollment.student_not_found 404 étudiant
enrollment.course_welcome sujet email
enrollment.course_welcome_body corps email

14. Ordre d’implémentation

Phase Livrable
1 DDL 03_academic_structure_api.sql + entités TypeORM
2 /levels, /academic-years, /departments (lecture)
3 /school-classes + /cohorts + membres
4 /programs CRUD + liste
5 /programs/:id/curriculum
6 /courses/:id/enrollments/* + emails
7 Tests intégration + Swagger tags AcademicStructure

15. Swagger

Tags OpenAPI :

  • Programs — formations / cursus
  • ProgramCurriculum — niveaux, semestres, UE
  • Levels — catalogue LMD
  • AcademicYears
  • SchoolClasses
  • Cohorts
  • CourseEnrollments — inscriptions groupées

Voir aussi

  • Schéma base de données : /database-schema
  • Concepts métier : /academic-concepts
  • Architecture : /architecture
  • Swagger interactif : /docs