Semaine 7

This commit is contained in:
gauvainboiche
2026-03-20 21:26:23 +01:00
parent 74d0c3f75b
commit 606e43e53f
69 changed files with 2343 additions and 0 deletions

View File

@@ -0,0 +1 @@
DB_PASSWORD=MementoMarsouin96

View File

@@ -0,0 +1 @@
3.12

View File

@@ -0,0 +1,4 @@
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=MementoMarsouin96
DB_NAME=petcare

View File

@@ -0,0 +1,23 @@
from peewee import *
import os
from dotenv import load_dotenv
load_dotenv()
db_name = os.environ.get("DB_NAME")
db_user = os.environ.get("DB_USER")
db_password = os.environ.get("DB_PASSWORD")
db_host = os.environ.get("DB_HOST")
db = MySQLDatabase(
database= db_name,
user= db_user,
password= db_password,
host= db_host,
port= 3306
)
class BaseModel(Model):
class Meta:
database = db

View File

@@ -0,0 +1,10 @@
from __init__ import *
class Care(BaseModel):
id = AutoField(primary_key= True)
nom = CharField(max_length= 150, null= False)
description = TextField()
prix = DecimalField(max_digits= 6, decimal_places= 2)
class Meta:
table_name = "traitement"

View File

@@ -0,0 +1,27 @@
from __init__ import *
from classes.pet import Pet
from classes.veterinarian import Veterinarian
from classes.care import Care
class Consultation(BaseModel):
id = AutoField(primary_key= True)
date_consultation = DateField(formats= '%Y-%m-%d') # formats= '%Y-%m-%d %H:%M:%S
motif = CharField(max_length= 255, null= False)
diagnostic = TextField()
prix_consultation = DecimalField(max_digits= 6, decimal_places= 2)
animal = ForeignKeyField(Pet, backref= "animal", null= False)
veterinaire = ForeignKeyField(Veterinarian, backref= "veterinaire", null= False)
class Meta:
table_name = "consultation"
class ConsultationCare(BaseModel):
consultation = ForeignKeyField(Consultation)
traitement = ForeignKeyField(Care)
class Meta:
table_name = "consultation_traitement"
indexes= (
(("consultation", "traitement"), True),
)

View File

@@ -0,0 +1,12 @@
from __init__ import *
class Owner(BaseModel):
id = AutoField(primary_key= True)
prenom = CharField(max_length= 100, null= False)
nom = CharField(max_length= 100, null= False)
telephone = CharField(max_length= 15, null= False)
email = CharField(max_length= 150, null= False, unique= True)
ville = CharField(max_length= 100, null= False)
class Meta:
table_name = "proprietaire"

View File

@@ -0,0 +1,15 @@
from __init__ import *
from classes.owner import Owner
class Pet(BaseModel):
id = AutoField(primary_key= True)
nom = CharField(max_length= 100, null= False)
espece = CharField(max_length= 50, null= False)
race = CharField(max_length= 100, null= False)
date_naissance = DateField(formats= '%Y-%m-%d')
poids = DecimalField(max_digits= 5, decimal_places= 2)
proprietaire = ForeignKeyField(Owner, backref= "proprietaire", null= False)
class Meta:
table_name = "animal"

View File

@@ -0,0 +1,11 @@
from __init__ import *
class Veterinarian(BaseModel):
id = AutoField(primary_key= True)
prenom = CharField(max_length= 100, null= False)
nom = CharField(max_length= 100, null= False)
specialite = CharField(max_length= 100, null= False, unique= True)
telephone = CharField(max_length= 15, null= False)
class Meta:
table_name = "veterinaire"

View File

@@ -0,0 +1,127 @@
from __init__ import *
# 1. Modèles
from classes.care import *
from classes.consultation import *
from classes.owner import *
from classes.pet import *
from classes.veterinarian import *
# 2. CRUD
## 1. CREATE
try:
with db.atomic():
owner_01 = Owner.create(
prenom= "Camille",
nom= "LEMOINE",
telephone= "0656781234",
email= "camille.lemoine@mail.com",
ville= "Bordeaux"
)
pet_01 = Pet.create(
nom= "Pixel",
espece= "Chat",
race= "Bengal",
date_naissance= "2023-06-15",
poids= 4.5,
proprietaire= owner_01
)
veterinarian_01_girard = Veterinarian.get(3)
consultation_01 = Consultation.create(
date_consultation= "2025-05-15",
motif= "Première visite",
diagnostic= "RAS",
prix_consultation= 35.00,
animal= pet_01,
veterinaire= veterinarian_01_girard
)
care_01_vermifuge = Care.get(Care.nom == "Vermifuge")
care_01_puce_elec = Care.get(10)
ConsultationCare.create(consultation= consultation_01, traitement= care_01_vermifuge)
ConsultationCare.create(consultation= consultation_01, traitement= care_01_puce_elec)
except IntegrityError as error:
print(error)
## 2. READ
print("\n----- Animaux de Claire MARTIN -----")
claire_martin_pets = Pet.select().join(Owner).where(
(Owner.prenom == "Claire") &
(Owner.nom == "Martin")
)
for pet in claire_martin_pets:
print(f"{pet.nom} - {pet.espece} - {pet.poids}")
print("\n----- Consultations de REX -----")
claire_martin_rex_consultations = Consultation\
.select(Consultation, Veterinarian)\
.join(Veterinarian)\
.where(Consultation.animal == 1)
for consultation in claire_martin_rex_consultations:
print(f"{consultation.date_consultation} - {consultation.motif} - Dr {consultation.veterinaire.nom}")
print("\n----- Statistiques par espèce -----")
# Partie faite à l'aide de l'IA, notamment pour le "group_by" avant quoi je n'avais que "Chien" sans explication
# et qui m'a soufflé l'idée de l'alias qui est quand même bien pratique
stats_species = Pet.select(
Pet.espece,
fn.COUNT(Pet.id).alias("nb_pets"),
fn.AVG(Pet.poids).alias("avg_weight")
).group_by(Pet.espece)
for stat in stats_species:
print(f"{stat.nb_pets} {stat.espece}(s) au total - {round(stat.avg_weight, 2)}kg en moyenne")
## 3. UPDATE
print("\n----- Le Gros Buddy qui mange trop -----")
try:
pet_buddy = Pet.get(Pet.nom == "Buddy")
print(f"Le poids de {pet_buddy.nom} est enregistré comme étant de {pet_buddy.poids}kg.")
pet_buddy.poids = 29.50
pet_buddy.save()
print(f"Le poids de Buddy a été changé.")
print(f"Le poids de {pet_buddy.nom} est enregistré comme étant de {pet_buddy.poids}kg.")
except DoesNotExist:
print("L'animal \"Buddy\" n'existe pas.")
## 4. DELETE
print("\n----- Vous z'avez pas vu mon fils ? -----")
try:
pet_nemo = Pet.get(Pet.nom == "Nemo")
had_nemo_consultations = Consultation.select().where(Consultation.animal == pet_nemo).count()
if had_nemo_consultations > 0:
print(f"Impossible de supprimer {pet_nemo.nom}, il possède des consultations associées.")
else:
pet_nemo.delete_instance()
print(f"L'animal {pet_nemo.nom} a été supprimé de la base de données.")
except DoesNotExist:
print(f"L'animal \"Nemo\" n'a pas été trouvé dans la base de données.")
# 3. Requêtes avancées
print("\n----- Top 3 soigneurs de zanimaux -----")
top_veterinarians = Veterinarian\
.select(
Veterinarian.nom,
Veterinarian.specialite,
fn.COUNT(Consultation.id).alias("nb_cons")
)\
.join(Consultation)\
.group_by(Veterinarian.id)\
.order_by(fn.COUNT(Consultation.id)\
.desc()).limit(3)
for idx, veterinarian in enumerate(top_veterinarians):
print(f"{idx+1} - Dr {veterinarian.nom} avec {veterinarian.nb_cons} consultations.")
print("\n----- Facture n°1 détaillée -----")
print("\n----- Historique-Animal -----")
def historique_animal():

View File

@@ -0,0 +1,142 @@
CREATE DATABASE IF NOT EXISTS petcare;
USE petcare;
CREATE TABLE proprietaire (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100) NOT NULL,
prenom VARCHAR(100) NOT NULL,
telephone VARCHAR(15) NOT NULL,
email VARCHAR(150) UNIQUE,
ville VARCHAR(100) NOT NULL
);
CREATE TABLE veterinaire (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100) NOT NULL,
prenom VARCHAR(100) NOT NULL,
specialite VARCHAR(100) NOT NULL,
telephone VARCHAR(15) NOT NULL UNIQUE
);
CREATE TABLE animal (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100) NOT NULL,
espece VARCHAR(50) NOT NULL,
race VARCHAR(100),
date_naissance DATE,
poids DECIMAL(5,2) CHECK (poids > 0),
proprietaire_id INT NOT NULL,
FOREIGN KEY (proprietaire_id) REFERENCES proprietaire(id)
);
CREATE TABLE consultation (
id INT AUTO_INCREMENT PRIMARY KEY,
date_consultation DATE NOT NULL,
motif VARCHAR(255) NOT NULL,
diagnostic TEXT,
prix_consultation DECIMAL(6,2) NOT NULL DEFAULT 35.00 CHECK (prix_consultation > 0),
animal_id INT NOT NULL,
veterinaire_id INT NOT NULL,
FOREIGN KEY (animal_id) REFERENCES animal(id),
FOREIGN KEY (veterinaire_id) REFERENCES veterinaire(id)
);
CREATE TABLE traitement (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(150) NOT NULL,
description TEXT,
prix DECIMAL(6,2) NOT NULL CHECK (prix > 0)
);
CREATE TABLE consultation_traitement (
consultation_id INT,
traitement_id INT,
dosage VARCHAR(100),
PRIMARY KEY (consultation_id, traitement_id),
FOREIGN KEY (consultation_id) REFERENCES consultation(id),
FOREIGN KEY (traitement_id) REFERENCES traitement(id)
);
-- Propriétaires
INSERT INTO proprietaire (nom, prenom, telephone, email, ville) VALUES
('Martin', 'Claire', '0612345678', 'claire.martin@mail.com', 'Paris'),
('Dupont', 'Jean', '0698765432', 'jean.dupont@mail.com', 'Lyon'),
('Bernard', 'Sophie', '0654321987', 'sophie.bernard@mail.com', 'Paris'),
('Petit', 'Thomas', '0687654321', 'thomas.petit@mail.com', 'Marseille'),
('Moreau', 'Julie', '0676543210', 'julie.moreau@mail.com', 'Lyon'),
('Garcia', 'Pierre', '0665432109', NULL, 'Toulouse'),
('Roux', 'Isabelle', '0643219876', 'isabelle.roux@mail.com', 'Paris');
-- Vétérinaires
INSERT INTO veterinaire (nom, prenom, specialite, telephone) VALUES
('Leroy', 'Marc', 'Chirurgie', '0611111111'),
('Fabre', 'Anne', 'Dermatologie', '0622222222'),
('Girard', 'Paul', 'Médecine générale', '0633333333'),
('Duval', 'Marie', 'Comportement', '0644444444');
-- Animaux
INSERT INTO animal (nom, espece, race, date_naissance, poids, proprietaire_id) VALUES
('Rex', 'Chien', 'Berger Allemand', '2020-03-15', 32.50, 1),
('Mimi', 'Chat', 'Siamois', '2019-07-22', 4.20, 1),
('Buddy', 'Chien', 'Labrador', '2021-01-10', 28.00, 2),
('Luna', 'Chat', 'Persan', '2022-05-18', 3.80, 3),
('Rocky', 'Chien', 'Bulldog', '2020-11-03', 22.50, 4),
('Nemo', 'Poisson', 'Poisson clown', '2023-01-01', 0.02, 4),
('Bella', 'Chien', 'Golden Retriever', '2019-09-14', 30.00, 5),
('Caramel', 'Chat', 'Européen', '2021-08-25', 5.10, 5),
('Max', 'Chien', 'Husky', '2022-02-28', 25.00, 6),
('Coco', 'Perroquet', 'Ara', '2018-04-12', 1.20, 7),
('Filou', 'Chat', 'Maine Coon', '2020-06-30', 7.50, 7),
('Oscar', 'Chien', 'Caniche', '2023-03-20', 8.00, 2);
-- Traitements
INSERT INTO traitement (nom, description, prix) VALUES
('Vaccination rage', 'Vaccin antirabique annuel', 45.00),
('Vaccination typhus', 'Vaccin contre le typhus félin', 40.00),
('Vermifuge', 'Traitement antiparasitaire interne', 25.00),
('Anti-puces', 'Traitement antiparasitaire externe', 30.00),
('Détartrage', 'Nettoyage dentaire sous anesthésie', 80.00),
('Radiographie', 'Examen radiologique', 60.00),
('Analyse sanguine', 'Bilan sanguin complet', 55.00),
('Antibiotiques', 'Traitement antibactérien 10 jours', 35.00),
('Castration', 'Stérilisation chirurgicale', 120.00),
('Puce électronique', 'Identification par puce', 50.00);
-- Consultations
INSERT INTO consultation (date_consultation, motif, diagnostic, prix_consultation, animal_id, veterinaire_id) VALUES
('2025-01-15', 'Visite annuelle', 'Bon état général', 35.00, 1, 1),
('2025-01-20', 'Boiterie', 'Entorse légère patte avant droite', 35.00, 3, 1),
('2025-02-10', 'Mauvaise haleine', 'Tartre important', 35.00, 2, 2),
('2025-02-14', 'Perte de poils', 'Allergie alimentaire', 45.00, 4, 2),
('2025-03-01', 'Vaccination', 'RAS', 35.00, 5, 3),
('2025-03-05', 'Griffures', 'Infection cutanée légère', 35.00, 8, 2),
('2025-03-10', 'Visite annuelle', 'Surpoids léger', 35.00, 7, 3),
('2025-03-15', 'Apathie', 'Anémie légère', 45.00, 11, 3),
('2025-04-01', 'Comportement agressif', 'Stress environnemental', 50.00, 9, 4),
('2025-04-10', 'Visite annuelle', 'Bon état général', 35.00, 10, 3),
('2025-04-15', 'Vomissements', 'Gastrite', 35.00, 12, 1),
('2025-04-20', 'Visite de contrôle', 'Guérison confirmée', 35.00, 3, 1),
('2025-05-01', 'Vaccination rappel', 'RAS', 35.00, 1, 3);
-- Consultations-Traitements
INSERT INTO consultation_traitement (consultation_id, traitement_id, dosage) VALUES
(1, 1, '1 injection'),
(1, 3, '1 comprimé'),
(2, 6, '2 clichés'),
(2, 8, '1 comprimé/jour pendant 5 jours'),
(3, 5, 'Séance complète'),
(4, 7, 'Prélèvement standard'),
(4, 4, 'Application locale'),
(5, 1, '1 injection'),
(5, 10, 'Implantation épaule gauche'),
(6, 8, '1 comprimé/jour pendant 7 jours'),
(6, 4, 'Application locale'),
(7, 3, '1 comprimé'),
(7, 7, 'Prélèvement standard'),
(8, 7, 'Prélèvement standard'),
(9, 7, 'Prélèvement standard'),
(10, 3, '1 comprimé'),
(11, 8, '1 comprimé/jour pendant 5 jours'),
(12, 6, '1 cliché'),
(13, 1, '1 injection'),
(13, 3, '1 comprimé');

View File

@@ -0,0 +1,4 @@
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=MementoMarsouin96
DB_NAME=datacenters

View File

@@ -0,0 +1,23 @@
from peewee import *
import os
from dotenv import load_dotenv
load_dotenv()
db_name = os.environ.get("DB_NAME")
db_user = os.environ.get("DB_USER")
db_password = os.environ.get("DB_PASSWORD")
db_host = os.environ.get("DB_HOST")
db = MySQLDatabase(
database= db_name,
user= db_user,
password= db_password,
host= db_host,
port= 3306
)
class BaseModel(Model):
class Meta:
database = db

View File

@@ -0,0 +1,11 @@
from __init__ import *
class Client(BaseModel):
id = AutoField(primary_key= True)
nom = CharField(max_length= 150, null= False)
email = CharField(max_length= 200, null= False)
telephone = CharField(max_length= 20, null= False)
type_contrat = CharField(max_length= 50, null= False)
class Meta:
table_name = "client"

View File

@@ -0,0 +1,12 @@
from __init__ import *
class Datacenter(BaseModel):
id = AutoField(primary_key= True)
nom = CharField(max_length= 100, null= False)
ville = CharField(max_length= 100, null= False)
pays = CharField(max_length= 100, null= False)
capacite_racks = IntegerField(null= False)
annee_ouverture = IntegerField(null= False)
class Meta:
table_name = "datacenter"

View File

@@ -0,0 +1,15 @@
from __init__ import *
from classes.client import Client
from classes.servers import Serveur
class Hebergement(BaseModel):
id = AutoField(primary_key= True)
date_debut = DateField(formats= "%Y-%m-%d", null= False)
date_fin = DateField(formats= "%Y-%m-%d", null= True)
prix_mensuel = DecimalField(max_digits= 8, decimal_places= 2, null= False)
serveur = ForeignKeyField(Serveur, backref= "serveur", null= False)
client = ForeignKeyField(Client, backref= "client", null= False)
class Meta:
table_name = "hebergement"

View File

@@ -0,0 +1,15 @@
from __init__ import *
from classes.servers import Serveur
class Intervention(BaseModel):
id = AutoField(primary_key= True)
date_intervention = DateField(formats= "%Y-%m-%d")
type_intervention = CharField(max_length= 100, null= False)
description = TextField()
duree_heures = DecimalField(max_digits= 4, decimal_places= 2, null= False)
cout = DecimalField(max_digits= 8, decimal_places= 2, null= False)
serveur = ForeignKeyField(Serveur, backref= "serveur", null= False)
class Meta:
table_name = "intervention"

View File

@@ -0,0 +1,14 @@
from __init__ import *
from classes.datacenter import Datacenter
class Salle(BaseModel):
id = AutoField(primary_key= True)
nom = CharField(max_length= 100, null= False)
etage = IntegerField(null= False)
superficie_m2 = DecimalField(max_digits= 7, decimal_places= 2, null= False)
nb_racks = IntegerField(null= False)
datacenter = ForeignKeyField(Datacenter, backref= "datacenter", null= False)
class Meta:
table_name = "salle"

View File

@@ -0,0 +1,17 @@
from __init__ import *
from classes.salle import Salle
class Serveur(BaseModel):
id = AutoField(primary_key= True)
hostname = CharField(max_length= 100, null= False)
marque = CharField(max_length= 100, null= False)
modele = CharField(max_length= 100, null= False)
cpu_cores = IntegerField(null= False)
ram_go = IntegerField(null= False)
stockage_to = DecimalField(max_digits= 5, decimal_places= 2, null= False)
date_installation = DateField(formats= "%Y-%m-%d")
salle = ForeignKeyField(Salle, backref= "salle", null= False)
class Meta:
table_name = "serveur"

View File

@@ -0,0 +1,115 @@
from __init__ import *
from classes.client import *
from classes.datacenter import *
from classes.hosting import *
from classes.intervention import *
from classes.salle import *
from classes.servers import *
# 1. Requêtes de base
print("\n----- Datacenters en France par année d'ouverture -----")
datacenters_france = (Datacenter
.select(Datacenter.nom, Datacenter.ville)
.where(Datacenter.pays == "France")
.order_by(Datacenter.annee_ouverture))
for datacenter in datacenters_france:
print(f"{datacenter.nom} - {datacenter.ville}")
print("\n----- Serveurs Dell par nom d'hôte -----")
dell_servers = (Serveur
.select(Serveur.hostname, Serveur.modele, Serveur.cpu_cores)
.where(Serveur.marque == 'Dell')
.order_by(Serveur.hostname))
for serveur in dell_servers:
print(f"{serveur.hostname} - Modèle {serveur.modele} - {serveur.cpu_cores} coeurs")
print("\n----- Interventions de mars 2025 -----")
march_2025_interventions = (Intervention
.select(Intervention.type_intervention, Intervention.cout, Intervention.date_intervention)
.where(Intervention.date_intervention.between('2025-03-01', '2025-03-31')))
for intervention in march_2025_interventions:
print(f"{intervention.date_intervention} : {intervention.type_intervention} pour {intervention.cout}€.")
# 2. Jointures
print("\n----- Liste des serveurs -----")
server_listing = (Serveur
.select(Serveur, Salle, Datacenter)
.join(Salle)
.join(Datacenter)
.order_by(Datacenter.nom, Serveur.hostname))
for server in server_listing:
print(f"{server.hostname} - Salle {server.salle.nom}, DC {server.salle.datacenter.nom}")
print("\n----- Hébergements actifs -----")
active_hostings = (Hebergement
.select(Hebergement, Client, Serveur)
.join(Client)
.switch(Hebergement)
.join(Serveur)
.where(Hebergement.date_fin.is_null())
.order_by(Hebergement.prix_mensuel.desc()))
for active_hosting in active_hostings:
print(f"{active_hosting.client.nom} - sur {active_hosting.serveur.hostname} pour {active_hosting.prix_mensuel}€ / mois.")
print("\n----- Serveurs qui n'ont jamais panné (ni poisson ni rien) -----")
servers_without_problem = (Serveur
.select(Serveur.hostname, Serveur.date_installation)
.join(Intervention, JOIN.LEFT_OUTER)
.where(Intervention.id.is_null()))
for server in servers_without_problem:
print(f"{server.hostname} - Installation {server.date_installation}.)")
print("\n----- Hébergements par client -----")
client_hostings = (Client
.select(Client.nom,
fn.COUNT(Hebergement.id).alias("nb_servers"),
fn.SUM(Hebergement.prix_mensuel).alias("monthly_price"))
.join(Hebergement)
.group_by(Client.id, Client.nom)
.order_by(fn.SUM(Hebergement.prix_mensuel).desc()))
for client_hosting in client_hostings:
total = client_hosting.monthly_price if client_hosting.monthly_price else 0
print(f"{client_hosting.nom} - {client_hosting.nb_servers} serveur(s) pour un total de {total}€ / mois.")
# 3. Agrégations
print("\n----- Liste des serveurs par marque -----")
servers_brand = (Serveur
.select(Serveur.marque, fn.COUNT(Serveur.id).alias("nb_servers"))
.group_by(Serveur.marque))
for server_brand in servers_brand:
print(f"{server_brand.marque} - {server_brand.nb_servers} serveur(s).")
print("\n----- Coût total par intervention -----")
intervention_cost = (Intervention
.select(Intervention.type_intervention,
fn.SUM(Intervention.cout).alias("cost"),
fn.AVG(Intervention.cout).alias("avg"))
.group_by(Intervention.type_intervention))
for cost in intervention_cost:
print(f"{cost.type_intervention} - {cost.cost}€ - moyenne à {round(cost.avg, 2)}€.") # Bonus pour le fun
print("\n----- Datacenters qui tournent pas trop à vide -----")
empty_datacenters = (Datacenter
.select(Datacenter.nom, fn.COUNT(Serveur.id).alias("nb_serveurs"))
.join(Salle)
.join(Serveur)
.group_by(Datacenter.id, Datacenter.nom)
.having(fn.COUNT(Serveur.id) > 3))
for datacenter in empty_datacenters:
print(f"{datacenter.nom} - {datacenter.nb_serveurs} serveurs.")
print("\n----- Mois le plus chargé de l'année -----")
heaviest_month = (Intervention
.select(fn.DATE_FORMAT(Intervention.date_intervention, '%Y-%m').alias('mois'),
fn.COUNT(Intervention.id).alias('nb_interventions'),
fn.SUM(Intervention.cout).alias('cout_total'))
.group_by(fn.DATE_FORMAT(Intervention.date_intervention, '%Y-%m'))
.order_by(fn.COUNT(Intervention.id).desc())
.limit(1))
for record in heaviest_month:
print(f"En {record.mois}, il y a eu {record.nb_interventions} interventions totalisant {record.cout_total}€.\n\
Pensez aux actionnaires !")

View File

@@ -0,0 +1,152 @@
CREATE DATABASE IF NOT EXISTS datacenters;
USE datacenters;
CREATE TABLE datacenter (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100) NOT NULL UNIQUE,
ville VARCHAR(100) NOT NULL,
pays VARCHAR(100) NOT NULL,
capacite_racks INT NOT NULL CHECK (capacite_racks > 0),
annee_ouverture INT NOT NULL CHECK (annee_ouverture >= 2000)
);
CREATE TABLE salle (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100) NOT NULL,
etage INT NOT NULL,
superficie_m2 DECIMAL(7,2) NOT NULL CHECK (superficie_m2 > 0),
nb_racks INT NOT NULL CHECK (nb_racks > 0),
datacenter_id INT NOT NULL,
FOREIGN KEY (datacenter_id) REFERENCES datacenter(id)
);
CREATE TABLE client (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(150) NOT NULL,
email VARCHAR(200) NOT NULL UNIQUE,
telephone VARCHAR(20),
type_contrat VARCHAR(50) NOT NULL CHECK (type_contrat IN ('Essentiel', 'Standard', 'Premium'))
);
CREATE TABLE serveur (
id INT AUTO_INCREMENT PRIMARY KEY,
hostname VARCHAR(100) NOT NULL UNIQUE,
marque VARCHAR(100) NOT NULL,
modele VARCHAR(100) NOT NULL,
cpu_cores INT NOT NULL CHECK (cpu_cores > 0),
ram_go INT NOT NULL CHECK (ram_go > 0),
stockage_to DECIMAL(5,2) NOT NULL CHECK (stockage_to > 0),
date_installation DATE NOT NULL,
salle_id INT NOT NULL,
FOREIGN KEY (salle_id) REFERENCES salle(id)
);
CREATE TABLE intervention (
id INT AUTO_INCREMENT PRIMARY KEY,
date_intervention DATE NOT NULL,
type_intervention VARCHAR(100) NOT NULL,
description TEXT,
duree_heures DECIMAL(4,2) NOT NULL CHECK (duree_heures > 0),
cout DECIMAL(8,2) NOT NULL CHECK (cout >= 0),
serveur_id INT NOT NULL,
FOREIGN KEY (serveur_id) REFERENCES serveur(id)
);
CREATE TABLE hebergement (
id INT AUTO_INCREMENT PRIMARY KEY,
date_debut DATE NOT NULL,
date_fin DATE,
prix_mensuel DECIMAL(8,2) NOT NULL CHECK (prix_mensuel > 0),
serveur_id INT NOT NULL,
client_id INT NOT NULL,
FOREIGN KEY (serveur_id) REFERENCES serveur(id),
FOREIGN KEY (client_id) REFERENCES client(id)
);
-- Datacenters
INSERT INTO datacenter (nom, ville, pays, capacite_racks, annee_ouverture) VALUES
('DC-Paris-Nord', 'Paris', 'France', 500, 2015),
('DC-Lyon-Est', 'Lyon', 'France', 300, 2018),
('DC-Marseille-Sud', 'Marseille', 'France', 200, 2020),
('DC-Bruxelles-Centre', 'Bruxelles', 'Belgique', 400, 2016),
('DC-Geneve-Lac', 'Genève', 'Suisse', 250, 2019);
-- Salles
INSERT INTO salle (nom, etage, superficie_m2, nb_racks, datacenter_id) VALUES
('Salle Alpha', 0, 450.00, 120, 1),
('Salle Beta', 0, 380.00, 100, 1),
('Salle Gamma', 1, 500.00, 140, 1),
('Salle Delta', 0, 300.00, 80, 2),
('Salle Epsilon', 1, 280.00, 70, 2),
('Salle Zeta', 0, 200.00, 50, 3),
('Salle Eta', 0, 350.00, 100, 4),
('Salle Theta', 1, 320.00, 90, 4),
('Salle Iota', 0, 250.00, 65, 5),
('Salle Kappa', 1, 230.00, 60, 5);
-- Clients
INSERT INTO client (nom, email, telephone, type_contrat) VALUES
('TechnoWeb SAS', 'contact@technoweb.fr', '0145678901', 'Premium'),
('StartupFlow', 'admin@startupflow.io', '0234567890', 'Standard'),
('MegaStore Europe', 'infra@megastore.eu', '0156789012', 'Premium'),
('DataLab Research', 'tech@datalab.org', '0145671234', 'Standard'),
('CloudFirst Agency', 'ops@cloudfirst.fr', '0198765432', 'Premium'),
('GreenEnergy Corp', 'it@greenenergy.com', '0187654321', 'Essentiel'),
('MediaPlus', 'support@mediaplus.fr', '0176543210', 'Standard'),
('FinanceSecure SA', 'infra@financesecure.ch', '0223456789', 'Premium'),
('EduConnect', 'admin@educonnect.fr', '0156784321', 'Essentiel'),
('LogiTrans SARL', 'it@logitrans.fr', '0167890123', 'Standard');
-- Serveurs
INSERT INTO serveur (hostname, marque, modele, cpu_cores, ram_go, stockage_to, date_installation, salle_id) VALUES
('srv-web-01', 'Dell', 'PowerEdge R750', 32, 128, 2.00, '2023-03-15', 1),
('srv-web-02', 'Dell', 'PowerEdge R750', 32, 128, 2.00, '2023-03-15', 1),
('srv-db-01', 'HP', 'ProLiant DL380', 64, 512, 8.00, '2022-06-10', 1),
('srv-app-01', 'Lenovo', 'ThinkSystem SR650', 48, 256, 4.00, '2023-09-01', 2),
('srv-storage-01', 'Dell', 'PowerEdge R760', 16, 64, 24.00, '2024-01-20', 3),
('srv-lyon-01', 'HP', 'ProLiant DL360', 32, 128, 2.00, '2023-05-12', 4),
('srv-lyon-02', 'HP', 'ProLiant DL380', 48, 256, 4.00, '2022-11-08', 4),
('srv-lyon-db-01', 'Dell', 'PowerEdge R750', 64, 512, 8.00, '2023-01-25', 5),
('srv-mars-01', 'Lenovo', 'ThinkSystem SR630', 16, 64, 1.00, '2024-03-10', 6),
('srv-mars-02', 'Lenovo', 'ThinkSystem SR650', 32, 128, 2.00, '2024-03-10', 6),
('srv-brux-01', 'Dell', 'PowerEdge R750', 48, 256, 4.00, '2021-07-15', 7),
('srv-brux-02', 'HP', 'ProLiant DL380', 64, 512, 8.00, '2021-07-15', 7),
('srv-brux-03', 'Dell', 'PowerEdge R760', 32, 128, 2.00, '2023-04-20', 8),
('srv-gen-01', 'HP', 'ProLiant DL380', 48, 256, 4.00, '2022-09-05', 9),
('srv-gen-02', 'Lenovo', 'ThinkSystem SR650', 32, 128, 2.00, '2023-08-18', 10);
-- Interventions
INSERT INTO intervention (date_intervention, type_intervention, description, duree_heures, cout, serveur_id) VALUES
('2025-01-10', 'Maintenance préventive', 'Remplacement disques préventif', 2.00, 350.00, 3),
('2025-01-15', 'Panne matérielle', 'Remplacement alimentation défectueuse', 1.50, 280.00, 1),
('2025-02-01', 'Mise à jour firmware', 'Update BIOS et firmware RAID', 1.00, 150.00, 6),
('2025-02-12', 'Panne matérielle', 'Remplacement barrette RAM défectueuse', 0.50, 420.00, 12),
('2025-02-20', 'Extension capacité', 'Ajout 256 Go RAM', 1.00, 1200.00, 7),
('2025-03-01', 'Maintenance préventive', 'Nettoyage et vérification câblage', 3.00, 200.00, 11),
('2025-03-05', 'Mise à jour firmware', 'Update BIOS', 0.50, 100.00, 4),
('2025-03-10', 'Panne matérielle', 'Remplacement ventilateur', 0.50, 180.00, 9),
('2025-03-15', 'Extension capacité', 'Ajout disque SSD 2 To', 1.00, 450.00, 14),
('2025-04-01', 'Maintenance préventive', 'Vérification générale', 2.00, 200.00, 1),
('2025-04-05', 'Panne matérielle', 'Remplacement contrôleur RAID', 2.50, 750.00, 8),
('2025-04-10', 'Mise à jour firmware', 'Update firmware stockage', 1.00, 150.00, 5),
('2025-04-15', 'Maintenance préventive', 'Remplacement pâte thermique CPU', 1.50, 120.00, 13),
('2025-05-01', 'Panne matérielle', 'Remplacement carte réseau', 1.00, 320.00, 2),
('2025-05-10', 'Extension capacité', 'Ajout 128 Go RAM', 0.50, 600.00, 10);
-- Hébergements
INSERT INTO hebergement (date_debut, date_fin, prix_mensuel, serveur_id, client_id) VALUES
('2023-04-01', NULL, 850.00, 1, 1),
('2023-04-01', NULL, 850.00, 2, 1),
('2022-07-01', NULL, 1500.00, 3, 3),
('2023-10-01', NULL, 1100.00, 4, 5),
('2024-02-01', NULL, 600.00, 5, 3),
('2023-06-01', '2025-01-31', 750.00, 6, 2),
('2023-02-01', NULL, 1100.00, 7, 4),
('2023-02-01', NULL, 1500.00, 8, 8),
('2024-04-01', NULL, 400.00, 9, 9),
('2024-04-01', NULL, 750.00, 10, 6),
('2021-08-01', NULL, 1100.00, 11, 3),
('2021-08-01', NULL, 1500.00, 12, 8),
('2023-05-01', NULL, 750.00, 13, 7),
('2022-10-01', NULL, 1100.00, 14, 5),
('2023-09-01', NULL, 750.00, 15, 10);

View File

@@ -0,0 +1,47 @@
# Evaluation 20/03/2026 - BOICHÉ Gauvain
## Démarrage
### Initialisation
```bash
uv init --python 3.12
uv run main.py
.venv/Scripts/activate
uv add dotenv
uv add peewee pymysql
```
### Fichier .env
Les "secrets" sont gérés pour l'occasion dans un fichier .env
Normalement, on met aussi un fichier
`.gitignore`
Dont le contenu mentionne :
`*.env`
Pour l'exercice évidemment, je garde le fichier en clair.
## Partie 1
## Partie 2
### Requêtes normales
Avoir une requête SQL normale fonctionne :
```sql
select nom, ville
from datacenters.datacenter
where pays = "France"
order by annee_ouverture;
```
Maintenant faut les traduire.

View File

@@ -0,0 +1,19 @@
services:
mysql:
image: mysql:8.0
container_name: mysql_evaluation
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
command: --log_bin_trust_function_creators=1
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
volumes:
mysql_data:

View File

@@ -0,0 +1,11 @@
[project]
name = "boiche-gauvain-eval-20-03-2026"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"dotenv>=0.9.9",
"peewee>=4.0.2",
"pymysql>=1.1.2",
]

View File

@@ -0,0 +1,58 @@
version = 1
revision = 3
requires-python = ">=3.12"
[[package]]
name = "boiche-gauvain-eval-20-03-2026"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "dotenv" },
{ name = "peewee" },
{ name = "pymysql" },
]
[package.metadata]
requires-dist = [
{ name = "dotenv", specifier = ">=0.9.9" },
{ name = "peewee", specifier = ">=4.0.2" },
{ name = "pymysql", specifier = ">=1.1.2" },
]
[[package]]
name = "dotenv"
version = "0.9.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "python-dotenv" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892, upload-time = "2025-02-19T22:15:01.647Z" },
]
[[package]]
name = "peewee"
version = "4.0.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/54/9b/3439dcc3ed5138eb26f28fc208f958ca97b831f5f898b26d5354302e5df4/peewee-4.0.2.tar.gz", hash = "sha256:fd5d026ce7787c6cd3a84316e665a76b72c9c4e87322598c413f44604fa6c38d", size = 714081, upload-time = "2026-03-15T15:46:30.049Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4f/8e/943f57ce32eb005c7037110a759e2ebfb22e467a9a0efcb6d516c5bf00c6/peewee-4.0.2-py3-none-any.whl", hash = "sha256:86d5d16bcda5bbe017a108f6efc57abaac8d89277915541904542df9d2a7f25d", size = 143344, upload-time = "2026-03-15T15:46:28.778Z" },
]
[[package]]
name = "pymysql"
version = "1.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f5/ae/1fe3fcd9f959efa0ebe200b8de88b5a5ce3e767e38c7ac32fb179f16a388/pymysql-1.1.2.tar.gz", hash = "sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03", size = 48258, upload-time = "2025-08-24T12:55:55.146Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7c/4c/ad33b92b9864cbde84f259d5df035a6447f91891f5be77788e2a3892bce3/pymysql-1.1.2-py3-none-any.whl", hash = "sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9", size = 45300, upload-time = "2025-08-24T12:55:53.394Z" },
]
[[package]]
name = "python-dotenv"
version = "1.2.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" },
]

View File

@@ -0,0 +1,5 @@
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=MementoMarsouin96
DB_NAME=company
DB_PORT=3306

View File

@@ -0,0 +1 @@
3.12

View File

@@ -0,0 +1,11 @@
Normalement, on met aussi un fichier
`.gitignore`
Dont le contenu mentionne :
```
*.env
```
Pour l'exercice évidemment, je garde le fichier en clair.

View File

@@ -0,0 +1,19 @@
services:
mysql:
image: mysql:8.0
container_name: mysql_peewee
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
command: --log_bin_trust_function_creators=1
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
volumes:
mysql_data:

View File

@@ -0,0 +1,123 @@
from peewee import *
# import os
# from dotenv import load_dotenv
# load_dotenv()
# db_name = os.environ.get("DB_NAME")
# db_user = os.environ.get("DB_USER")
# db_password = os.environ.get("DB_PASSWORD")
# db_host = os.environ.get("DB_HOST")
# db_port = os.environ.get("DB_PORT")
db_name = "company"
db_user = "root"
db_password = "MementoMarsouin96"
db_host = "localhost"
db_port = 3306
db = MySQLDatabase(
database= db_name,
user= db_user,
password= db_password,
host= db_host,
port= db_port
)
# équivalent de "Base = declarative_base()"
class BaseModel(Model):
class Meta:
database = db
class Department(BaseModel):
department_id = AutoField(primary_key= True)
department_name = CharField(max_length= 50, null= False, unique= True)
location_id = DecimalField(max_digits= 4, decimal_places= 0, null= False)
# équivalent de "__tablename__ = "truc""
class Meta:
table_name = "departments"
class Employee(BaseModel):
employee_id = AutoField(primary_key= True)
first_name = CharField(max_length= 50, null= False)
last_name = CharField(max_length= 50, null= False)
email = CharField(max_length= 50, null= False, unique= True)
salary = IntegerField(null= False)
department = ForeignKeyField(Department, backref= "employees", null= False)
class Meta:
table_name = "employees"
db.connect()
db.create_tables([Department, Employee])
try:
it_dept = Department.create(department_name= "IT", location_id= 1400)
except IntegrityError:
print("Département déjà existant.")
it_dept = Department.get(Department.department_name == "IT")
try:
Employee.create(
first_name= "Alice",
last_name= "Smith",
email= "a.smith@company.au",
salary= 4000,
department= it_dept # utiliser l'OBJET, pas le nom de l'objet
)
except IntegrityError:
print("Employé(e) déjà existant(e).")
new_employees = [
{'first_name': 'Marie', 'last_name': 'Dupont', 'email': 'mdupont', 'salary': 4500},
{'first_name': 'Jean', 'last_name': 'Martin', 'email': 'jmartin', 'salary': 5200},
{'first_name': 'Sophie', 'last_name': 'Bernard', 'email': 'sbernard', 'salary': 4800},
]
# with db.atomic():
# Employee.insert_many(new_employees).execute()
employee_01 = Employee.get(1)
print(employee_01.first_name, employee_01.last_name)
employee_02 = Employee.get(Employee.email == "a.smith@company.au")
print(employee_02.first_name, employee_02.salary)
try:
employee_76 = Employee.get(76)
except DoesNotExist:
print("L'employé(e) 76 n'existe pas.")
employee_select_01 = Employee.select() # SELECT * FROM employees
print(employee_select_01)
for employee in employee_select_01:
print(employee.first_name, employee.last_name, employee.email)
employee_select_02 = Employee.select().where(
(Employee.salary > 8000) & (Employee.department == it_dept)
)
print(employee_select_02)
# pagination
page_01 = Employee.select().order_by(Employee.employee_id).paginate(1, 10)
# compter une valeur
salary_01 = Employee.select().where(Employee.salary > 10000).count()
# Jointures (LEFT JOIN, RIGHT JOIN etc)
### FULL JOIN
query = (Employee
.select(Employee, Department)
.join(Department)
.where(Department.department_name == "IT"))
### Employés affectés à un département
### et ceux non affectés à un département
query = (Employee
.select(Employee, Department)
.join(Department, JOIN.LEFT_OUTER)
.where(Department.department_name == "IT"))

View File

@@ -0,0 +1,10 @@
[project]
name = "orm-peewee"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"peewee>=4.0.2",
"pymysql>=1.1.2",
]

View File

@@ -0,0 +1,105 @@
from peewee import *
# import os
# from dotenv import load_dotenv
# load_dotenv()
# db_name = os.environ.get("DB_NAME")
# db_user = os.environ.get("DB_USER")
# db_password = os.environ.get("DB_PASSWORD")
# db_host = os.environ.get("DB_HOST")
# db_port = os.environ.get("DB_PORT")
db_name = "company"
db_user = "root"
db_password = "MementoMarsouin96"
db_host = "localhost"
db_port = 3306
db = MySQLDatabase(
database= db_name,
user= db_user,
password= db_password,
host= db_host,
port= db_port
)
# équivalent de "Base = declarative_base()"
class BaseModel(Model):
class Meta:
database = db
# Relations One-To-Many
class User(BaseModel):
username = CharField(unique= True)
email = CharField()
class Meta:
table_name = "user"
class Todo(BaseModel):
title = CharField()
is_done = BooleanField(default= False)
user= ForeignKeyField(User, backref= "todos") # l'accès inverse est géré automatiquement
class Meta:
table_name = "todos"
class Tag(BaseModel):
name = CharField(unique= True)
class Meta:
table_name = "tag"
class TodoTag(BaseModel):
todo = ForeignKeyField(Todo)
tag = ForeignKeyField(Tag)
class Meta:
table_name = 'todo_tag'
indexes= (
(('todo', 'tag'), True),
)
class Profile(BaseModel):
user = ForeignKeyField(User, unique= True, backref= "profile") # One-To-One
bio = TextField(null= True)
avatar = CharField(null= True)
class Meta:
table_name = "profile"
db.connect()
db.create_tables([User, Todo, Profile, Tag, TodoTag])
try:
alice = User.create(username= "Alice", email= "a.smith@company.au")
Todo.create(title= "Apprendre Python", user= alice)
Todo.create(title= "Apprendre JS", user= alice)
Todo.create(title= "Apprendre COBOL", user= alice)
for todo in alice.todos:
print(todo.title, todo.is_done)
except IntegrityError as error:
print(error)
try:
Profile.create(
user= User.get(1),
bio = 'Developpeur',
avatar= 'http://example.com'
)
except IntegrityError as error:
print(error)
try:
tag_urgent = Tag.create(name= "Urgent")
tag_perso = Tag.create(name= "Perso")
tag_pro = Tag.create(name= "Pro")
todo_cobol = Todo.get(Todo.title == "Apprendre COBOL")
TodoTag.create(todo= todo_cobol, tag= tag_urgent)
except IntegrityError as error:
print(error)

36
Semaine_07/Jour_05/orm_peewee/uv.lock generated Normal file
View File

@@ -0,0 +1,36 @@
version = 1
revision = 3
requires-python = ">=3.12"
[[package]]
name = "orm-peewee"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "peewee" },
{ name = "pymysql" },
]
[package.metadata]
requires-dist = [
{ name = "peewee", specifier = ">=4.0.2" },
{ name = "pymysql", specifier = ">=1.1.2" },
]
[[package]]
name = "peewee"
version = "4.0.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/54/9b/3439dcc3ed5138eb26f28fc208f958ca97b831f5f898b26d5354302e5df4/peewee-4.0.2.tar.gz", hash = "sha256:fd5d026ce7787c6cd3a84316e665a76b72c9c4e87322598c413f44604fa6c38d", size = 714081, upload-time = "2026-03-15T15:46:30.049Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4f/8e/943f57ce32eb005c7037110a759e2ebfb22e467a9a0efcb6d516c5bf00c6/peewee-4.0.2-py3-none-any.whl", hash = "sha256:86d5d16bcda5bbe017a108f6efc57abaac8d89277915541904542df9d2a7f25d", size = 143344, upload-time = "2026-03-15T15:46:28.778Z" },
]
[[package]]
name = "pymysql"
version = "1.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f5/ae/1fe3fcd9f959efa0ebe200b8de88b5a5ce3e767e38c7ac32fb179f16a388/pymysql-1.1.2.tar.gz", hash = "sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03", size = 48258, upload-time = "2025-08-24T12:55:55.146Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7c/4c/ad33b92b9864cbde84f259d5df035a6447f91891f5be77788e2a3892bce3/pymysql-1.1.2-py3-none-any.whl", hash = "sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9", size = 45300, upload-time = "2025-08-24T12:55:53.394Z" },
]