Corrigé bac NSI 2026 Métropole jour 1 — Exercice 3 : Plateforme de débats : arbre des arguments en POO et base de données SQL
Sujet officiel du baccalauréat, spécialité numérique et sciences informatiques, session 2026. Corrigé rédigé par Ibrahim Alame.
Travailler ce sujet sur Adloun Sujet officiel (PDF) Corrigé complet (PDF)
Énoncé
Cet exercice porte sur les arbres, l'algorithmique des arbres, et les bases de données.
On s'intéresse au fonctionnement interne d'une plateforme en ligne dédiée spécifiquement aux débats. Plutôt que de lister des messages des utilisateurs dans l'ordre chronologique, les différentes contributions sont structurées de façon arborescente afin de mieux comprendre les liens entre les arguments. Chaque contribution vient se rattacher à une affirmation, soit afin d'appuyer cette affirmation avec un argument pour, soit afin de l'attaquer avec un argument contre.
Cet exercice contient deux parties indépendantes l'une de l'autre.
Partie A : structure arborescente des arguments
Dans cette partie on modélise un arbre de débat pour un sujet de débat. La figure 1 montre un exemple d'arbre de débat.
Figure 1. Arbre de débat sur l'objectivité de la science
Dans un arbre de débat, chaque nœud est une affirmation, et il y en a de trois sortes :
- le sujet du débat (dans le parallélogramme) est une affirmation, c'est le point de départ du débat ;
- un argument pour (dans un rectangle aux coins arrondis) peut venir soutenir une affirmation, cet argument est lui-même une affirmation ;
- un argument contre (dans un hexagone) peut venir attaquer une affirmation, cet argument est lui-même une affirmation.
De plus, chaque affirmation peut être likée (j'aime) ou dislikée (je n'aime pas) par les utilisateurs.
1. Indiquer le nom donné au nœud « point de départ du débat » dans le vocabulaire des arbres.
2. Indiquer en justifiant si un arbre de débat est un arbre binaire ou non.
On modélise la structure d'arbre de débat à l'aide de la classe suivante :
class Affirmation:
def __init__(self, phrase):
"""
Création d'un objet Affirmation à partir d'une
phrase (str).
"""
self.contenu = phrase
self.sorte = "" # vaudra "sujet", "pour" ou "contre"
# vaut "" avant ajout dans l'arbre
self.arguments = []
self.nb_likes = 0
self.nb_dislikes = 0
3. Donner le nom et le type de chaque attribut de la classe Affirmation.
On souhaite ajouter à cette classe une méthode soutenir qui permet d'ajouter un argument pour à l'affirmation.
On suppose à disposition une méthode contrer similaire qui permet d'ajouter un argument contre à l'affirmation.
def soutenir(self, argument):
"""
Ajoute l'Affirmation `argument` comme argument pour de
l'Affirmation `self`.
Précondition : l'Affirmation ne doit pas avoir déjà été
utilisée, ni comme sujet, ni comme pour, ni comme
contre, ce qu'on vérifie grâce à son attribut `sorte`.
"""
assert argument.sorte == ...
... .append(argument)
argument.sorte = "..."
4. Recopier et compléter les lignes 10 à 12 de la méthode soutenir, en respectant sa documentation.
Pour mesurer les débats les plus vifs, la plateforme de débats dispose de plusieurs mesures.
5. Écrire la méthode nb_contre qui permet d'obtenir le nombre d'arguments contre directement rattachés à cette Affirmation.
On s'intéresse à la méthode mystere suivante, qui utilise la méthode nb_contre :
def mystere(self):
m = self.nb_contre()
for arg in self.arguments:
candidat = arg.mystere()
if candidat > m:
m = candidat
return m
6. Expliquer par une phrase ce que renvoie la méthode mystere.
Afin de trancher le débat dans sa globalité, on souhaite évaluer une Affirmation, en tenant compte de la totalité des likes et des dislikes de chaque argument, et en tenant compte du fait que ces arguments soient pour ou contre. Si l'évaluation est positive le pour l'emporte, si elle est négative le contre l'emporte.
La formule pour l'évaluation d'une affirmation correspond à la somme de ses likes et des évaluations de ses arguments pour, à laquelle on soustrait ses dislikes et les évaluations de ses arguments contre.
7. Écrire, en utilisant uniquement des lignes de code choisies parmi les lignes suivantes, une méthode evaluation qui renvoie le résultat du calcul décrit dans la formule ci-dessus. Les lignes ne sont pas toutes à utiliser et l'indentation est à adapter.
def evaluation(self):
def evaluation():
total = self.nb_likes + self.nb_dislikes
total = self.nb_likes - self.nb_dislikes
total = self.nb_dislikes - self.nb_likes
total = self.nb_likes * self.nb_dislikes
for arg in self.arguments:
for arg in self.arguments_pour:
for arg in self.arguments_contre:
if arg.sorte == "sujet":
if arg.sorte == "pour":
if arg.sorte == "contre":
else:
total = arg.evaluation()
total = total + arg.evaluation()
total = total - arg.evaluation()
total = total * arg.evaluation()
return total
return 1 + total
Partie B : base de données des utilisateurs et de leurs contributions
Dans cette partie, on pourra utiliser les clauses du langage SQL pour :
- construire des requêtes d'interrogation à l'aide de
SELECT,FROM,WHERE(avec les opérateurs logiquesAND,OR) etJOIN ... ON; - construire des requêtes d'insertion et de mise à jour à l'aide de
UPDATE,INSERTetDELETE.
Pour manipuler toutes les informations sur plusieurs sujets de débats, la plateforme dispose d'un Système de Gestion de Bases de Données (SGBD).
8. Parmi les propositions suivantes, indiquer celles qui font partie des rôles d'un SGBD :
- [a)] sécuriser les accès à la base de données ;
- [b)] assurer l'alimentation électrique des serveurs ;
- [c)] assurer la persistance des données même en cas de panne matérielle ;
- [d)] gérer les accès en parallèle de plusieurs utilisateurs ;
- [e)] assurer des connexions en HTTPS au serveur.
Pour gérer les données, le schéma relationnel utilisé est représenté sur la figure 2 (transcrit ici en notation textuelle : clés primaires soulignées, clés étrangères précédées d'un « # ») :
utilisateur(<u>pseudo</u>, email, nom, prenom)
affirmation(<u>id_aff</u>, contenu, #auteur, sorte, #aff_repondue, nb_likes, nb_dislikes)
reaction(<u>#aff_reagie</u>, <u>#utilisateur</u>, sorte)
Flèches du schéma : affirmation.auteur et reaction.utilisateur référencent utilisateur.pseudo ; affirmation.aff_repondue et reaction.aff_reagie référencent affirmation.id_aff.
Figure 2. Schéma relationnel de la plateforme de débat
La clé primaire de chaque table correspond à l'ensemble de ses attributs soulignés. Chaque attribut précédé d'un « # » est une clé étrangère.
L'attribut sorte de la table affirmation peut valoir 'sujet' quand c'est l'affirmation initiale d'un sujet de débat, ou bien 'pour' ou 'contre' quand c'est une réponse à une autre affirmation (référencée par l'attribut aff_repondue). L'attribut contenu correspond à la phrase elle-même de l'affirmation.
L'attribut sorte de la table reaction peut valoir 'like' ou 'dislike', et s'applique à l'affirmation référencée par l'attribut aff_reagie.
Voici des exemples d'extraits des tables affirmation et reaction. On a omis, pour ces exemples, les attributs auteur et contenu de la table affirmation :
| `affirmation` | ||||
|---|---|---|---|---|
| `id_aff` | `sorte` | `aff_repondue` | `nb_likes` | `nb_dislikes` |
| 0 | `'sujet'` | `NULL` | 42 | 17 |
| 1 | `'pour'` | 0 | 20 | 20 |
| 2 | `'pour'` | 0 | 40 | 10 |
| 3 | `'contre'` | 0 | 13 | 12 |
| 4 | `'contre'` | 0 | 18 | 6 |
| 7 | `'contre'` | 1 | 15 | 16 |
| `reaction` | ||
|---|---|---|
| `aff_reagie` | `utilisateur` | `sorte` |
| 0 | `'alice'` | `'like'` |
| 1 | `'alice'` | `'dislike'` |
| 0 | `'bob'` | `'dislike'` |
| 1 | `'bob'` | `'dislike'` |
9. Indiquer en justifiant quel autre attribut de la table utilisateur aurait aussi pu servir de clé primaire.
On rappelle que la fonction d'agrégation COUNT permet de compter les éléments du résultat d'une requête en plaçant COUNT(*) dans la clause SELECT.
10. Écrire une requête SQL permettant d'obtenir le nombre d'affirmations qui ont 50 likes ou plus.
11. Écrire une requête qui permet d'obtenir les contenus des sujets et leur nombre de likes, créés par les utilisateurs dont les prénom et nom sont respectivement 'Pierre' et 'Durand'.
Le mot-clé AS permet de donner un autre nom à une table au sein d'une requête. Il est notamment nécessaire lorsqu'on a besoin de faire la jointure d'une table avec elle-même. On considère la requête SQL suivante :
SELECT aff.contenu, rep.contenu
FROM affirmation AS aff JOIN affirmation AS rep
ON rep.affirmation_repondue = aff.id_affirmation
WHERE rep.nb_likes >= 2 * aff.nb_likes
AND rep.sorte = 'contre'
12. Donner une interprétation en français de ce que permet d'obtenir la requête ci-dessus.
L'utilisateur au pseudo 'i<3descartes' approuve par un like l'affirmation 'Cogito ergo sum' qui a l'id_aff 108.
13. Recopier et compléter les deux requêtes SQL suivantes, permettant de mettre à jour les informations de la base de données suite à cette action.
... reaction
VALUES (..., ..., ...);;
... affirmation
SET nb_likes = ...
WHERE ... = ...;;
L'utilisateur au pseudo 'i<3rgpd' souhaite faire valoir son droit à l'effacement, et supprimer son compte utilisateur et toutes les données qui y sont liées. Dans cette situation, la plateforme de débat, qui souhaite maintenir la qualité des échanges, suit la procédure suivante :
- elle anonymise toutes les affirmations de l'utilisateur (en remplaçant l'attribut
auteurpar la valeurNULL), considérant que les affirmations elle-mêmes ne sont pas des données personnelles ; - elle supprime toutes les réactions de l'utilisateur (mais sans toucher au nombres de likes et de dislikes des affirmations correspondantes) ;
- et enfin elle supprime l'utilisateur lui-même.
14. Expliquer pourquoi il est nécessaire de supprimer les réactions de l'utilisateur avant de supprimer l'utilisateur.
15. Écrire les trois requêtes qui permettent la suppression des données du compte de 'i<3rgpd' en suivant la procédure décrite ci-dessus.
Corrigé
Partie A : structure arborescente des arguments
1. Le point de départ du débat est la racine de l'arbre : l'unique nœud sans parent, dont tous les autres nœuds descendent. Sur la figure 1, c'est le parallélogramme « La Science est une activité humaine objective. » ; les arguments sans réponse (par exemple « Les géants pétroliers… ») en sont les feuilles.
2. Un arbre binaire est un arbre dont chaque nœud possède au plus deux enfants (un sous-arbre gauche et un sous-arbre droit). Ce n'est pas le cas d'un arbre de débat : une affirmation peut recevoir autant d'arguments qu'on veut. Sur la figure 1, la racine a quatre enfants (deux arguments pour et deux arguments contre) et l'affirmation « La recherche financée par des organismes commerciaux peut tordre les faits. » en a trois. Un arbre de débat est donc un arbre général (dit « quelconque »), et non un arbre binaire. Dans la classe Affirmation, cela se traduit par l'attribut arguments, une liste d'enfants de longueur quelconque, là où un arbre binaire aurait deux attributs gauche et droit.
3. Les attributs sont ceux créés dans le constructeur __init__ :
| Attribut | Type | Rôle |
|---|---|---|
| `contenu` | `str` | la phrase de l'affirmation |
| `sorte` | `str` | `""`, puis `"sujet"`, `"pour"` ou `"contre"` |
| `arguments` | `list` (d'objets `Affirmation`) | les arguments rattachés, vide au départ |
| `nb_likes` | `int` | nombre de likes, 0 au départ |
| `nb_dislikes` | `int` | nombre de dislikes, 0 au départ |
4. La documentation impose de vérifier que l'argument n'a encore jamais été utilisé : avant tout ajout, son attribut sorte vaut la chaîne vide (ligne 8 du constructeur). On l'ajoute ensuite à la liste des arguments de self, puis on le marque comme argument pour :
assert argument.sorte == ""
self.arguments.append(argument)
argument.sorte = "pour"
Testé : après s.soutenir(a), a figure dans s.arguments et a.sorte vaut "pour" ; un second s.soutenir(a) lève une AssertionError, conformément à la précondition.
Ce que le correcteur attend : c'est self.arguments qui reçoit l'argument (et non argument.arguments), et c'est argument.sorte qui change (pas self.sorte, qui reste ce qu'il était).
5. On parcourt les arguments directement rattachés (la liste arguments, sans descendre plus bas dans l'arbre) et l'on compte ceux dont la sorte est "contre" :
def nb_contre(self):
n = 0
for arg in self.arguments:
if arg.sorte == "contre":
n = n + 1
return n
Sur l'arbre de la figure 1 (reconstruit et exécuté) : la racine renvoie 2, « La Science se base sur les faits… » renvoie 1, et une feuille renvoie 0.
6. La méthode mystere renvoie le plus grand nombre d'arguments contre directement rattachés à une même affirmation, parmi l'affirmation self et toutes celles de son sous-arbre (ses arguments, les arguments de ses arguments, etc.) : c'est le « maximum de contre-attaques directes » subies par une affirmation du débat.
En effet, m part de self.nb_contre(), puis, pour chaque argument, l'appel récursif arg.mystere() fournit le maximum sur le sous-arbre de cet argument, et m ne conserve que le plus grand de tous ces candidats. La récursion s'arrête d'elle-même sur les feuilles, dont la boucle ne fait aucun tour. Sur la figure 1, mystere appelée sur la racine renvoie 2 (la racine subit deux arguments contre, aucune autre affirmation n'en subit plus d'un).
7. L'évaluation vaut (likes dislikes) de l'affirmation, plus l'évaluation de chaque argument pour, moins l'évaluation de chaque argument contre ; c'est une définition récursive, l'appel arg.evaluation() descendant dans le sous-arbre :
def evaluation(self):
total = self.nb_likes - self.nb_dislikes
for arg in self.arguments:
if arg.sorte == "pour":
total = total + arg.evaluation()
else:
total = total - arg.evaluation()
return total
Le else est légitime car un argument rattaché ne peut être que "pour" ou "contre" ; on peut aussi remplacer else: par if arg.sorte == "contre":. Les autres lignes proposées sont à écarter : def evaluation(): n'a pas le paramètre self ; nb_likes + nb_dislikes et le produit ne suivent pas la formule ; arguments_pour et arguments_contre ne sont pas des attributs de la classe ; if arg.sorte == "sujet" ne peut jamais être vrai pour un argument ; total = arg.evaluation() écraserait le total au lieu de l'accumuler ; et return 1 + total ajouterait 1 à chaque nœud.
Vérification par exécution sur l'extrait de table de la partie B, qui décrit un petit arbre (0 est le sujet, 1 et 2 ses arguments pour, 3 et 4 ses arguments contre, 7 un argument contre de 1) : ; ; ; ; ; d'où : le pour l'emporte.
Partie B : base de données des utilisateurs et de leurs contributions
8. Les rôles d'un SGBD sont a), c) et d).
- a) Oui : le SGBD gère les droits d'accès (qui peut lire, modifier, supprimer quelles tables) et authentifie les utilisateurs de la base.
- b) Non : l'alimentation électrique relève de l'infrastructure matérielle (onduleurs, salle serveurs), pas d'un logiciel.
- c) Oui : la persistance est le cœur du SGBD ; il écrit les données sur disque et garantit, grâce à ses journaux et ses transactions, qu'une panne en cours d'opération ne laisse pas la base dans un état incohérent.
- d) Oui : le SGBD gère les accès concurrents de plusieurs utilisateurs (ou programmes) sans corrompre les données.
- e) Non : HTTPS est un protocole de la couche application du web, assuré par le serveur web ; le SGBD n'y intervient pas.
9. L'attribut email aurait aussi pu servir de clé primaire : une adresse électronique est propre à une personne, deux comptes ne peuvent pas partager la même, et elle est nécessairement renseignée à l'inscription ; elle identifie donc chaque n-uplet de façon unique et sans valeur NULL, ce qu'exige une clé primaire. Ni nom ni prenom ne conviennent : plusieurs utilisateurs peuvent s'appeler Pierre Durand.
10. On compte les lignes de affirmation dont le nombre de likes est au moins 50 :
SELECT COUNT(*)
FROM affirmation
WHERE nb_likes >= 50;
Sur l'extrait donné, le résultat serait 0 (le maximum est 42) ; vérifiée sur une base d'essai, la requête renvoie bien le nombre de lignes à nb_likes .
11. Le nom et le prénom sont dans utilisateur, le contenu et les likes dans affirmation : il faut une jointure sur la clé étrangère auteur, qui référence pseudo, puis filtrer les sujets et l'identité de l'auteur :
SELECT affirmation.contenu, affirmation.nb_likes
FROM affirmation
JOIN utilisateur ON affirmation.auteur = utilisateur.pseudo
WHERE affirmation.sorte = 'sujet'
AND utilisateur.prenom = 'Pierre'
AND utilisateur.nom = 'Durand';
Ce que le correcteur attend : la condition sorte = 'sujet' (on veut les sujets, pas tous les messages de Pierre Durand), la jointure (le nom n'est pas dans affirmation) et les deux conditions liées par AND.
12. La table affirmation est jointe avec elle-même : aff désigne une affirmation et rep une réponse qui lui est rattachée (sa clé étrangère « affirmation répondue » vaut l'identifiant de aff). Les conditions gardent les réponses de sorte 'contre' dont le nombre de likes est au moins le double de celui de l'affirmation attaquée. La requête renvoie donc, pour chaque argument contre au moins deux fois plus liké que l'affirmation qu'il attaque, le contenu de cette affirmation et le contenu de l'argument contre : elle repère les contre-arguments nettement plus populaires que ce qu'ils contredisent.
Remarque : la requête imprimée nomme les attributs affirmation_repondue et id_affirmation, alors que le schéma de la figure 2 les appelle aff_repondue et id_aff ; avec les noms du schéma, la jointure s'écrit ON rep.aff_repondue = aff.id_aff. Sur l'extrait de table, aucune ligne ne ressort : l'argument 7 (15 likes) attaque l'affirmation 1 (20 likes), et 3 et 4 (13 et 18 likes) attaquent 0 (42 likes) ; aucun n'atteint le double.
13. On ajoute une réaction (les valeurs dans l'ordre des attributs de la table : aff_reagie, utilisateur, sorte) puis on incrémente le compteur de likes de l'affirmation 108 :
INSERT INTO reaction
VALUES (108, 'i<3descartes', 'like');
UPDATE affirmation
SET nb_likes = nb_likes + 1
WHERE id_aff = 108;
Testé sur une base d'essai : la ligne (108, 'i<3descartes', 'like') apparaît dans reaction et nb_likes de l'affirmation 108 passe de 60 à 61.
Ce que le correcteur attend : nb_likes = nb_likes + 1 (on ne connaît pas la valeur actuelle, on ne peut pas écrire un nombre), et la clause WHERE sur id_aff, sans laquelle toutes les affirmations gagneraient un like.
14. Dans la table reaction, l'attribut utilisateur est une clé étrangère qui référence utilisateur.pseudo. La contrainte d'intégrité référentielle impose que toute valeur d'une clé étrangère corresponde à une clé primaire existante. Si l'on supprimait d'abord l'utilisateur, ses réactions référenceraient un pseudo qui n'existe plus : le SGBD refuse cette suppression (erreur de contrainte de clé étrangère, ce que nous avons observé sur une base d'essai). Il faut donc d'abord faire disparaître toutes les références au pseudo (les réactions, et l'attribut auteur des affirmations, mis à NULL), puis seulement supprimer l'utilisateur.
15. Les trois requêtes, dans l'ordre de la procédure :
UPDATE affirmation
SET auteur = NULL
WHERE auteur = 'i<3rgpd';
DELETE FROM reaction
WHERE utilisateur = 'i<3rgpd';
DELETE FROM utilisateur
WHERE pseudo = 'i<3rgpd';
La première anonymise les affirmations sans les supprimer (leurs nb_likes et nb_dislikes sont intacts) ; la deuxième supprime les réactions sans toucher aux compteurs des affirmations, comme demandé ; la troisième supprime enfin le compte, ce que le SGBD accepte maintenant que plus rien ne le référence. Exécutées dans cet ordre sur une base d'essai, elles laissent les affirmations de 'i<3rgpd' avec un auteur NULL et leurs compteurs inchangés, et suppriment l'utilisateur.