Maths & NSI

Baccalauréat — Épreuve pratique — 2026 — NSI

Épreuve pratique NSI 2026 — Sujet 15 : rappels de vaccination d'un cabinet vétérinaire

Sujet

Épreuve pratique de NSI, session 2026 — sujet n°15 de la banque nationale. Durée : 1 heure, sur ordinateur. Le candidat dispose de l'énoncé et de fichiers de code et de données ; il agit en autonomie et, aux « appels professeur » indiqués, présente son travail à l'examinateur ou le sollicite en cas de difficulté.

Rappels de vaccination d'un cabinet vétérinaire

Cadre. Un cabinet vétérinaire utilise une base de données pour assurer le suivi et la gestion des clients et animaux qui y sont suivis, dans le but d'améliorer la qualité des soins et de fournir des services utiles, que ce soit pour le vétérinaire ou pour ses clients.

Objectif. La vaccination régulière des chats (une fois par an) étant importante mais souvent oubliée par les propriétaires, le vétérinaire souhaiterait pouvoir envoyer des messages de rappel de vaccination par SMS, deux mois avant l'échéance (la date à laquelle la vaccination devrait avoir lieu). Il dispose pour cela d'une plateforme d'envoi de SMS, mais il est nécessaire de préparer les données et les messages à envoyer.

Description de la base de données

Le logiciel mis en œuvre par le cabinet utilise 3 tables pour enregistrer les animaux, leur propriétaire et les consultations. Schéma de la base de données :

  • consultation (id : int, id_animal : int, date : str (AAAAMMJJ), motif : str) — clé primaire id, clé étrangère id_animal ;
  • animal (id : int, espece : str, nom : str, date_naissance : str (AAAAMMJJ), id_proprietaire : int) — clé primaire id, clé étrangère id_proprietaire ;
  • proprietaire (id : int, nom : str, prenom : str, telephone : str) — clé primaire id.

consultation.id_animal fait référence à animal.id, et animal.id_proprietaire à proprietaire.id.

Extraits de la base de données :

idnomprenomtelephone
1DuhamelFrançois06-57-84-52-41
2DumontMarcelle0648747223
3JoubertAnouk0 4 76 33 78 15
idid_animaldatemotif
1120171229vaccination
25420050621bilan
34520131106maladie
idespecenomdate_naissanceid_proprietaire
1oiseauFlocon201711111
2lapinRoudoudou202109012
3chatMistral202512062

Le système de gestion de base de données utilisé est SQLite, et il est possible d'interagir avec la base de données en utilisant le module sqlite3, disponible dans la bibliothèque standard Python.

Rappels techniques

La connexion à la base de données se fait en précisant l'emplacement du fichier de la base de données. Par exemple, si le fichier cabinet.sqlite se trouve dans le répertoire courant, on écrit :

DB_PATH = "cabinet.sqlite"
conn = sqlite3.connect(DB_PATH)

Une fois que l'on dispose d'une connexion (variable conn), on utilise un curseur (variable cursor) pour exécuter des requêtes sur la base de données et éventuellement récupérer les résultats de la requête.

cursor = conn.cursor()
resultats = cursor.execute(
    "SELECT id, nom FROM animal ORDER BY nom LIMIT 10"
)
# resultats permet de récupérer les lignes de manière
# progressive, sous forme d'une liste itérable de tuples
# correspondant aux champs demandés
# Pour accéder aux champs sélectionnés
for r in resultats:
    # les valeurs renvoyées par le SELECT sont
    # accessibles par leur position
    print("id:", r[0], ", nom:", r[1])

Lorsqu'un paramètre doit être donné dans une requête SQL, il est fourni sous forme d'un ? dans le texte de la requête SQL, et passé en argument à la fonction execute sous forme d'un tuple (chaque ? correspond à un élément du tuple, dans l'ordre). Par exemple, pour récupérer les chats du propriétaire d'id 2 :

resultat = cursor.execute(
    """SELECT nom FROM animal
    WHERE espece = 'chat' AND id_proprietaire = ?""",
    (2,)
)

Les clauses du langage SQL à utiliser sont : SELECT, FROM, JOIN, ON, WHERE, ORDER BY.

Numéros de téléphone

La plateforme d'envoi de SMS requiert un numéro de téléphone au format 06XXXXXXXX ou 07XXXXXXXX, 10 chiffres au total, sans espace, ni point, ni autre caractère. Malheureusement, les téléphones ont été enregistrés dans la base sans distinguer les téléphones mobiles des téléphones fixes, et en utilisant différents styles comme « (0)6 12 34 56 78 » ou « 01.23.45.67.89 ».

Rappel : on peut utiliser la méthode isdigit de la classe str pour déterminer si un caractère est un chiffre.

>>> '2'.isdigit()
True
>>> 'C'.isdigit()
False

Question 1. Écrire une fonction Python normalisation_tel qui prend un numéro de téléphone tel de type str en argument et qui renvoie le numéro de téléphone nettoyé en ne gardant que les chiffres. Exemples :

normalisation_tel("(0)2 12 99 90 12")  # renvoie "0212999012"
normalisation_tel("0.6.12.99.90.12")   # renvoie "0612999012"
normalisation_tel("0-6-12-9-90-1")     # renvoie "06129901"

Une fonction test_normalisation_tel est fournie.

Appel professeur — Appeler le professeur pour lui présenter votre réponse ou en cas de difficulté.

Validation des numéros de téléphone

La fonction validation_tel(tel) prend comme argument un numéro de téléphone nettoyé, et renvoie un booléen selon que ce numéro est un numéro de téléphone portable valide ou non.

Question 2. Écrire un jeu de tests permettant de vérifier le bon fonctionnement de la fonction.

Appel professeur — Appeler le professeur pour lui présenter votre réponse ou en cas de difficulté.

Détermination de la liste des chats à vacciner

La vaccination se faisant tous les ans, le vétérinaire souhaite envoyer trois rappels aux propriétaires. Chaque rappel sera donc envoyé aux propriétaires des chats ayant été vaccinés pour la dernière fois il y a entre 10 et 13 mois. Pour cela on commencera par déterminer quels chats ont été vaccinés il y a moins de 13 mois, à l'aide de la fonction consultation_vaccination_chat(date). Puis à l'aide de la fonction derniere_vaccination(consultations), on déterminera la date de dernière vaccination de ces chats. Enfin, dans la dernière étape du projet, se fera le filtrage des chats ayant été vaccinés pour la dernière fois il y a plus de 10 mois.

Interrogation de la base de données. Pour interroger la base de données, on utilise la fonction consultation_vaccination_chat(date) qui doit récupérer toutes les consultations de motif vaccination des chats (espèce : chat) du cabinet, pour des dates supérieures à une date date exprimée au format AAAAMMJJ (ce format permet de comparer des dates en utilisant l'ordre alphabétique). On souhaite récupérer les champs suivants : id de l'animal, nom de l'animal, téléphone du propriétaire, date de la consultation, triés par le champ id d'animal puis date de consultation croissants.

Question 3. En vous inspirant de la fonction proprietaires_animaux_nes_apres(date), écrire la fonction consultation_vaccination_chat(date). Des tests sont fournis dans la fonction test_consultation_vaccination_chat ; votre fonction doit les passer.

Appel professeur — Appeler le professeur pour lui présenter votre réponse ou en cas de difficulté.

Détermination de la date de dernière vaccination. Pour déterminer la date de dernière vaccination, on utilise la fonction derniere_vaccination qui prend en paramètre une liste de consultations avec les champs précédents (id de l'animal, nom de l'animal, téléphone du propriétaire, date de la consultation). Elle renvoie un dictionnaire associant comme clé l'id de chaque chat avec les informations de sa dernière (plus récente) consultation.

Exemple : pour Plume et Gollum,

(16, 'Plume', '0.6.36.96.89.83', '20241024'),
(16, 'Plume', '0.6.36.96.89.83', '20251125'),
(17, 'Gollum', '0.6.36.96.89.83', '20250113'),

la dernière consultation de Plume est le 20251125, et pour Gollum, c'est le 20250113 : derniere_vaccination doit donc renvoyer le dictionnaire

{
    16: (16, 'Plume', '0.6.36.96.89.83', '20251125'),
    17: (17, 'Gollum', '0.6.36.96.89.83', '20250113')
}

Cependant, la fonction ne produit pas les résultats attendus : les tests fournis dans la fonction test_derniere_vaccination échouent.

Question 4. Expliquer le problème rencontré, puis comment corriger cette fonction pour qu'elle passe les tests avec succès.

Appel professeur — Appeler le professeur pour lui présenter votre réponse ou en cas de difficulté.

Fichiers fournis

Le dossier comporte une version PDF de l'énoncé, le code source de départ veto.py et la base de données cabinet.sqlite (400 propriétaires, 411 animaux, 4 457 consultations). Le module sqlite3 doit être disponible.

veto.py

# -----------------------------------------------------------------------------
# Numéros de téléphones
# Question 1 : nettoyage des numéros de téléphone
 
# Écrire la fonction normalisation_tel ici
 
 
import sqlite3
 
 
def test_normalisation_tel():
    """
    Tous les tests doivent passer...
    """
    assert normalisation_tel("06 12 99 90 12") == "0612999012"
    assert normalisation_tel("02 12 99 90 12") == "0212999012"
    assert normalisation_tel("02.12.99.90.12") == "0212999012"
    assert normalisation_tel("0.6.12.99.90.12") == "0612999012"
    assert normalisation_tel("06-12-99-90-12") == "0612999012"
    assert normalisation_tel("(0)6.12.99-90-12 gilbert") == "0612999012"
    assert normalisation_tel("061299901") == "061299901"
    assert normalisation_tel("06129990123") == "06129990123"
    print('Les tests de la fonction normalisation_tel sont passés')
 
 
# -----------------------------------------------------------------------------
# Question 2 : validation des numéros de téléphone
 
 
def validation_tel(tel):
    """
    Validation des numéros de téléphone portable français
    selon les conditions spécifiées.
    :param tel: numéro de téléphone
    :return: True si le numéro est valide, False sinon
    """
    if len(tel) != 10:
        return False
    if tel[0] != "0":
        return False
    if tel[1] != "6" and tel[1] != "7":
        return False
    return True
 
 
# Ecrire votre jeu de tests permettant
# de vérifier le bon fonctionnement de la fonction.
 
 
# -----------------------------------------------------------------------------
# Détermination de la liste des chats à vacciner
# Question 3 : interrogation de la base de données
 
DB_PATH = "cabinet.sqlite"
 
 
def proprietaires_animaux_nes_apres(date):
    """
    Renvoie les noms et prénoms des propriétaires d'animaux nés après la date `date`,
    triés par ordre alphabétique de noms puis prénoms
 
    :param: date: date de naissance minimale
    :return: liste [(nom_proprietaire, prenom_ proprietaire)]
    """
    conn = sqlite3.connect(DB_PATH)
    cursor = conn.cursor()
    resultat = cursor.execute(
        """
    SELECT proprietaire.nom, proprietaire.prenom
    FROM proprietaire
        JOIN  animal ON proprietaire.id = animal.id_proprietaire 
    WHERE animal.date_naissance > ?
    ORDER BY proprietaire.nom, proprietaire.prenom;
    """,
        (date,),
    )
    return list(resultat)
 
 
def consultation_vaccination_chat(date):
    """
    Renvoie les consultations de vaccination de chats dont la date
    est supérieure à la date `date`.
 
    :param: date: date minimale
    :return: liste [(id_animal, nom_animal, tel_proprietaire, date_consultation)]
    """
    pass
 
 
def test_consultation_vaccination_chat():
    vaccinations = consultation_vaccination_chat("20240923")
    assert len(vaccinations) == 118
    assert vaccinations[0] == (16, "Plume", "0.6.36.96.89.83", "20241024")
    assert vaccinations[1] == (16, "Plume", "0.6.36.96.89.83", "20251125")
    assert vaccinations[2] == (17, "Gollum", "0.6.36.96.89.83", "20250113")
    assert vaccinations[3] == (26, "Olympe", "(0)4 73 98 01 23", "20250109")
    assert vaccinations[4] == (32, "Chopin", "06.37.97.66.64", "20241201")
    assert vaccinations[5] == (32, "Chopin", "06.37.97.66.64", "20251119")
    assert vaccinations[6] == (34, "Jazz", "0.6.37.51.65.52", "20250801")
    assert vaccinations[7] == (35, "Tango", "0324182", "20250706")
    assert vaccinations[8] == (38, "Loulou", "05-35-95-87-54", "20250209")
 
    # Vérification stricte du tri (ORDER BY id_animal, date_consultation)
    assert vaccinations == sorted(vaccinations, key=lambda x: (x[0], x[3]))
 
    print('Les tests de la fonction consultation_vaccination_chat sont passés')
 
# test_consultation_vaccination_chat()
 
# -----------------------------------------------------------------------------
# Question 4 : détermination de la date de dernière vaccination
 
 
def derniere_vaccination(consultations):
    """
    Renvoie un dictionnaire ayant pour clef l'identifiant de l'animal,
    et dont la valeur associée est la dernière consultation de cet animal.
 
    Chaque consultation est un tuple :
 
    (id_animal, nom_animal, tel_proprietaire, date_consultation)
    """
    derniere = {}
    for consult in consultations:
        id_animal = consult[0]
        date = consult[3]
        if id_animal not in derniere:
            derniere[id_animal] = consult
        elif date < derniere[id_animal][3]:
            derniere[id_animal] = consult
    return derniere
 
 
def test_derniere_vaccination():
    consultations_pour_test = [
        (16, "Plume", "0.6.36.96.89.83", "20241024"),
        (16, "Plume", "0.6.36.96.89.83", "20251125"),
        (17, "Gollum", "0.6.36.96.89.83", "20250113"),
        (26, "Olympe", "(0)4 73 98 01 23", "20250109"),
        (32, "Chopin", "06.37.97.66.64", "20241201"),
        (32, "Chopin", "06.37.97.66.64", "20251119"),
        (34, "Jazz", "0.6.37.51.65.52", "20250801"),
        (35, "Tango", "0324182", "20250706"),
        (38, "Loulou", "05-35-95-87-54", "20250209"),
        (39, "Tango", "07.45.48.02.42", "20250329"),
        (40, "Sésame", "07.45.48.02.42", "20250228"),
        (41, "Pixel", "0130709285", "20241204"),
        (41, "Pixel", "0130709285", "20251222"),
    ]
 
    resultat = derniere_vaccination(consultations_pour_test)
    # Affichage des résultats, décommenter si besoin
    # for a,r in resultat.items():
    #     print(a, ":", r)
 
    assert derniere_vaccination(consultations_pour_test) == {
        16: (16, "Plume", "0.6.36.96.89.83", "20251125"),
        17: (17, "Gollum", "0.6.36.96.89.83", "20250113"),
        26: (26, "Olympe", "(0)4 73 98 01 23", "20250109"),
        32: (32, "Chopin", "06.37.97.66.64", "20251119"),
        34: (34, "Jazz", "0.6.37.51.65.52", "20250801"),
        35: (35, "Tango", "0324182", "20250706"),
        38: (38, "Loulou", "05-35-95-87-54", "20250209"),
        39: (39, "Tango", "07.45.48.02.42", "20250329"),
        40: (40, "Sésame", "07.45.48.02.42", "20250228"),
        41: (41, "Pixel", "0130709285", "20251222"),
    }
    print('Les tests de la fonction derniere_vaccination sont passés')

Corrigé

Corrigé réservé aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

Sujet officiel de la banque nationale de sujets 2026 de l'épreuve pratique de NSI (ministère de l'Éducation nationale, sujets.examens-concours.gouv.fr). Corrigé rédigé pour ce site.