Dans ce tutoriel, tu vas créer une application de diagnostic PC avec Flask et Python : informations système (CPU, RAM, disques, réseau), liste des processus avec possibilité de les arrêter, programmes installés et formulaire de support. Un outil de helpdesk concret, basé sur psutil et Bootstrap.

Niveau : intermédiaireDurée : ~1 h 30Python · Flask · psutil · BootstrapCode source gratuit

Salut, c’est repartie pour un nouveau tutoriel sur Flask et Python. Dans ce tutoriel, je te guide pas à pas dans la création d’une application web professionnelle de diagnostic système pour techniciens IT. Tu apprendras à créer une interface moderne permettant de monitorer, gérer et diagnostiquer des PC Windows à distance.

Application de diagnostic PC avec Flask et Python : informations système CPU, RAM et disques

Introduction : une application de diagnostic PC avec Flask

En tant que développeur et technicien IT depuis plusieurs années, j’ai souvent eu besoin d’outils pour diagnostiquer rapidement les problèmes sur les postes de travail Windows. Les solutions existantes sont soit trop complexes, soit payantes, soit ne répondent pas exactement aux besoins.

C’est pourquoi j’ai décidé de créer HelpDesk Info, une application web légère et moderne qui permet de:

– ✅ Collecter toutes les informations système en un clin d’œil

– ✅ Gérer les programmes installés (avec désinstallation à distance)

– ✅ Contrôler les services Windows

– ✅ Monitorer les processus en temps réel

– ✅ Exporter des rapports de diagnostic

Ce que tu vas construire

Coté backend, nous verrons comment créer une application Flask structurée et professionnelle, comment utiliser psutil pour collecter des informations système, comment interagir avec le registre Windows via winreg, comment exécuter des commandes PowerShell depuis Python, comment Implémenter une gestion d’erreurs robuste et enfin comment configurer un système de logging professionnel.

Côté frontend, nous verrons comment créer une interface moderne avec Bootstrap 5, comment implémenter des animations et transitions CSS, comment utiliser DataTables pour des tableaux interactifs et enfin comment créer un système de notifications toast personnalisé

Nous utiliserons une architecture MVC pour applications Flask, la sécurisation des entrées utilisateur, et de stechniques de gestion des erreurs et exceptions

Architecture de l'application de diagnostic PC : frontend Bootstrap et backend Flask avec psutil
Liste des processus en cours dans l'application de diagnostic PC Flask

Étape 1 : Mise en place de l’environnement

1) Créer le projet

Ouvre un terminal et crée le dossier principal:

mkdir helpdesk-info
cd helpdesk-info
terminal

2) Créer / activer l’environnement virtuel (Windows)

Un environnement virtuel isole les dépendances de ton projet:

python -m venv venv
venv\Scripts\activate
terminal

3) Installer les dépendances

Crée le fichier fichier requirements.txt

Flask>=3.0.0
psutil>=5.9.0
requests>=2.31.0
requirements.txt

Puis installe les dépendances:

pip install -r requirements.txt
terminal

Ces étapes peuvent se faire depuis ton terminal ou ton invite de commandes sous Windows

Étape 2 : Structure du projet

Arborescence :

helpdesk-info/ ├── static/ │ └── style.css ├── templates/ │ └── index.html ├── main.py └── requirements.txt

Création rapide (Windows) :

mkdir static templates
type nul > main.py
type nul > static\style.css
type nul > templates\index.html
terminal

Étape 3 : Backend Flask (collecte système)

1) Base Flask + imports

Ouvre main.py et commençons par les imports et la configuration. Dans main.py, initialise Flask, les imports et les logs :

"""
HelpDesk Info - Application de diagnostic PC professionnel
Auteur: Michael - LM-Code (https://lm-code.be)
"""

from flask import Flask, render_template, request, send_file, jsonify
import platform
import socket
import psutil
import requests
import datetime
import os
import subprocess
import winreg
import logging
from io import BytesIO

# Configuration de l'application Flask
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
app.config['JSON_AS_ASCII'] = False

# Configuration du système de logs
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('helpdesk.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)
main.py

SECRET_KEY sécurise les sessions Flask, JSON_AS_ASCII=False permet les caractères accentués dans le JSON et logging crée un fichier de log pour tracer les actions.

2) Fonctions clés

  • get_size() : convertit les octets (RAM, disque…)
  • get_system_info() : récupère hostname, OS, CPU, RAM, disques, réseau, IP publique (ipify)
def get_size(bytes_value, suffix="B"):
    """
    Convertit une taille en octets vers un format lisible.

    Exemple: 1536 → "1.50 KB"
    """
    factor = 1024
    for unit in ["", "K", "M", "G", "T"]:
        if bytes_value < factor:
            return f"{bytes_value:.2f} {unit}{suffix}"
        bytes_value /= factor
    return f"{bytes_value:.2f} P{suffix}"
main.py

3) Route principale /

La fonction la plus importante : elle collecte toutes les données:

def get_system_info():
    """
    Collecte complète des informations système.

    Returns:
        dict: Toutes les informations système formatées
    """
    # IP publique via service externe
    try:
        public_ip = requests.get("https://api.ipify.org", timeout=5).text
        logger.info(f"IP publique récupérée: {public_ip}")
    except Exception as e:
        logger.warning(f"Impossible de récupérer l'IP publique: {e}")
        public_ip = "Non disponible"

    # Temps de démarrage et uptime
    boot_time = datetime.datetime.fromtimestamp(psutil.boot_time())
    uptime = datetime.datetime.now() - boot_time

    # Informations des disques
    disk_info = []
    for part in psutil.disk_partitions():
        try:
            usage = psutil.disk_usage(part.mountpoint)
            disk_info.append({
                "device": part.device,
                "mountpoint": part.mountpoint,
                "fstype": part.fstype,
                "total": get_size(usage.total),
                "used": get_size(usage.used),
                "free": get_size(usage.free),
                "percent": usage.percent
            })
        except PermissionError:
            continue

    # Informations réseau
    net_info = []
    mac_address = "Non disponible"
    for interface_name, interface_addresses in psutil.net_if_addrs().items():
        for address in interface_addresses:
            if str(address.family) == 'AddressFamily.AF_INET':
                net_info.append({
                    "interface": interface_name,
                    "ip": address.address,
                    "netmask": address.netmask if address.netmask else "N/A"
                })

    # Dictionnaire de retour
    return {
        "hostname": socket.gethostname(),
        "username": os.getlogin(),
        "os": f"{platform.system()} {platform.release()}",
        "architecture": platform.machine(),
        "cpu": platform.processor(),
        "cores_physical": psutil.cpu_count(logical=False),
        "cores_total": psutil.cpu_count(logical=True),
        "cpu_usage": psutil.cpu_percent(interval=1),
        "ram_total": get_size(psutil.virtual_memory().total),
        "ram_used": get_size(psutil.virtual_memory().used),
        "ram_available": get_size(psutil.virtual_memory().available),
        "boot_time": boot_time.strftime("%Y-%m-%d %H:%M:%S"),
        "uptime": str(uptime).split('.')[0],
        "ip_public": public_ip,
        "mac_address": mac_address,
        "disks": disk_info,
        "network": net_info
    }
main.py
  • GET : affiche les infos systèm
  • POST : récupère le texte “problème” décrit par l’utilisateur
  • psutil.cpu_percent(): Utilisation CPU en temps réel
  • psutil.virtual_memory(): Statistiques mémoire RAM
  • psutil.disk_partitions(): Liste des disques
  • Gestion des erreurs avec try/except pour éviter les crashs

4) Lancer l’app

En bas du fichier :

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=5000, debug=True, threaded=True)
main.py

Étape 4 : Frontend (Bootstrap + onglets)

Dans templates/index.html :

  • Charger Bootstrap 5.3.2 + DataTables + Bootstrap Icons + ton CSS
  • Créer un header (card + dégradé)
  • Ajouter des onglets :
    • Infos Système
    • Programmes
    • Services
    • Processus

Dans l’onglet “Infos Système” :

  • affichage des champs (readonly)
  • textarea “Décrivez votre problème”
  • boutons : Exporter / Envoyer

Étape 5 : CSS

Dans static/style.css :

  • variables CSS (couleurs, ombres, transitions)
  • fond en dégradé
  • header avec effet shimmer
  • onglets stylisés
  • boutons avec hover + ombre

Étape 6 : Programmes (Windows Registry)

Backend :

  • get_installed_programs() via winreg
  • endpoint /programs qui renvoie JSON

Frontend :

  • table DataTables #programsTable
  • chargement via jQuery $.getJSON('/programs')

Étape 7 : Services Windows (PowerShell)

Backend :

  • get_services() via Get-Service
  • endpoint /services (JSON)
  • endpoint /service_action (POST) : start/stop/restart avec validation + logs

Frontend :

  • table DataTables + badges Running/Stopped
  • boutons actions : start/stop/restart

Étape 8 : Processus (psutil)

Backend :

def get_processes():
    """Récupère la liste des processus actifs"""
    processes = []

    for proc in psutil.process_iter(['pid', 'name', 'cpu_percent', 'memory_info']):
        try:
            info = proc.info
            info['memory'] = get_size(info['memory_info'].rss)
            del info['memory_info']
            processes.append(info)
        except (psutil.NoSuchProcess, psutil.AccessDenied):
            continue

    return processes

@app.route("/processes")
def processes():
    """Endpoint API pour les processus"""
    try:
        return jsonify(get_processes())
    except Exception as e:
        logger.error(f"Erreur: {e}")
        return jsonify([]), 500
main.py
  • endpoint /processes (JSON)
  • endpoint /kill_process (POST) : terminate/kill avec confirmation + gestion AccessDenied
@app.route("/kill_process", methods=["POST"])
def kill_process():
    """Termine un processus par son PID"""
    try:
        data = request.get_json()

        if not data or 'pid' not in data:
            return jsonify({"success": False, "message": "PID manquant"}), 400

        pid = int(data.get("pid"))

        if not psutil.pid_exists(pid):
            return jsonify({"success": False, "message": "Processus inexistant"}), 404

        logger.info(f"Terminaison du processus PID: {pid}")

        process = psutil.Process(pid)
        process_name = process.name()

        process.terminate()

        try:
            process.wait(timeout=5)
            return jsonify({
                "success": True,
                "message": f"Processus '{process_name}' (PID: {pid}) terminé"
            })
        except psutil.TimeoutExpired:
            process.kill()
            return jsonify({
                "success": True,
                "message": f"Processus forcé à se terminer"
            })

    except psutil.AccessDenied:
        return jsonify({
            "success": False,
            "message": "Accès refusé. Privilèges administrateur requis."
        }), 403
    except Exception as e:
        logger.error(f"Erreur: {e}")
        return jsonify({"success": False, "message": str(e)}), 500
main.py
⚠️
Sécurité

Cette application peut lire les informations de ton système et terminer des processus. Garde-la en local (127.0.0.1) et ne l’expose jamais sur Internet, surtout avec debug=True, qui permet d’exécuter du code à distance.

Frontend :

  • table DataTables + mise en couleur CPU
  • bouton “Tuer” (confirm)
// Fonction de terminaison de processus
function killProcess(pid, name) {
  if (!confirm(`Terminer "${name}" (PID: ${pid}) ?\n\nCela peut causer une perte de données.`)) {
    return;
  }

  showToast(`Arrêt du processus ${pid} en cours...`, 'info');

  fetch('/kill_process', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ pid })
  })
  .then(res => res.json())
  .then(data => {
    if (data.success) {
      showToast(data.message, 'success');
      setTimeout(() => $('#proc-tab').click(), 1500);
    } else {
      showToast(data.message, 'error');
    }
  });
}

// Chargement des processus
$('#proc-tab').on('shown.bs.tab', () => {
  loadTable('/processes', '#processesTable', [
    { data: 'pid' },
    { data: 'name' },
    {
      data: 'cpu_percent',
      render: function(data) {
        const percentage = parseFloat(data) || 0;
        let colorClass = 'text-success';
        if (percentage > 50) colorClass = 'text-warning';
        if (percentage > 80) colorClass = 'text-danger';
        return `<span class="${colorClass} fw-bold">${percentage.toFixed(1)}%</span>`;
      }
    },
    { data: 'memory' },
    {
      data: null,
      render: function(data, type, row) {
        return `<button class="btn btn-sm btn-danger" onclick="killProcess(${row.pid}, '${row.name}')" title="Terminer">
          <i class="bi bi-x-circle"></i> Tuer
        </button>`;
      }
    }
  ]);
});
templates/index.html

Étape 9 : Export du rapport

Backend :

  • endpoint /export
  • génère un .txt (infos + listes) en mémoire (BytesIO)
  • retourne send_file(..., as_attachment=True)
Liste des programmes installés dans l'application de diagnostic PC Flask

Et voilà, notre projet est prêt. Merci d’avoir suivi ce tutoriel jusqu’au bout! J’espère que tu as appris des techniques utiles pour tes futurs projets. N’hésite pas à adapter ce projet à tes besoins spécifiques. Laisse tes avis en commentaire ou si tu as des questions.

Il est important de télécharger le code source tout en suivant le tuto.

Pour aller plus loin

FAQ : questions fréquentes

L’application fonctionne-t-elle sur macOS et Linux ?

Les informations système et les processus fonctionnent partout grâce à psutil. La liste des programmes installés utilise le registre Windows (winreg) et ne fonctionne donc que sous Windows.

Pourquoi certains processus ne peuvent pas être arrêtés ?

Les processus système ou ceux d’un autre utilisateur nécessitent des droits administrateur. L’application intercepte l’erreur AccessDenied et affiche un message au lieu de planter.

Puis-je mettre cette application en ligne ?

Non, elle est conçue pour un usage local : elle donne accès aux informations de la machine et peut arrêter des processus. Garde-la sur 127.0.0.1 et désactive le mode debug hors développement.

Comment obtenir l’adresse IP publique ?

L’application interroge le service ipify. Sans connexion Internet, cette information ne s’affiche simplement pas.

Comments

No comments yet. Why don’t you start the discussion?

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *