PFEE/dashboard-sqdc/database/populate_db.py
paul.roost ca05e334a7 feat: Implement server-side API for categories, KPIs, and measurements
- Added Express server with SQLite database connection.
- Created API endpoints to fetch categories, KPIs, measurements, and statistics.
- Implemented error handling for database operations.

feat: Create ChartModal component for visualizing KPI data

- Developed ChartModal to display line charts for KPI measurements.
- Integrated Chart.js for rendering charts with responsive design.
- Added styling for modal and chart components.

feat: Add ExportModal component for exporting KPI data

- Implemented ExportModal to allow users to select data ranges for export.
- Included radio buttons for predefined time ranges (last week, month, year, all data).
- Styled modal for better user experience.

feat: Introduce RangeChartModal for dynamic range selection

- Created RangeChartModal to visualize KPI data over user-selected time ranges.
- Integrated radio buttons for selecting different time ranges.
- Enhanced chart rendering with Chart.js.

refactor: Create useSQLiteDatabase hook for data fetching

- Developed custom hook to manage fetching categories, KPIs, and measurements.
- Improved error handling and loading states for better user feedback.

style: Add CSS styles for modals and charts

- Created styles for ChartModal, ExportModal, and RangeChartModal.
- Ensured responsive design for various screen sizes.
2025-10-21 13:31:14 +02:00

204 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
import sqlite3
import random
from datetime import datetime, timedelta
import math
# Configuration de la base de données
DB_PATH = 'database/sqdc.db'
# Catégories
CATEGORIES = [
{'id': 1, 'name': 'Sécurité', 'emoji': '🛡️', 'description': 'Indicateurs de sécurité et prévention des accidents'},
{'id': 2, 'name': 'Qualité', 'emoji': '🎯', 'description': 'Indicateurs de qualité des produits'},
{'id': 3, 'name': 'Délais & Livraison', 'emoji': '⏱️', 'description': 'Indicateurs de délais et livraison'},
{'id': 4, 'name': 'Coûts', 'emoji': '💰', 'description': 'Indicateurs de coûts de production'},
{'id': 5, 'name': 'Maintenance', 'emoji': '🔧', 'description': 'Indicateurs de maintenance'}
]
# KPIs avec fréquences
KPIS = [
# SÉCURITÉ (1 mesure par jour)
{'id': 1, 'category_id': 1, 'name': 'Taux de Fréquence (TF)', 'unit': 'par 1M heures', 'target': 1.0, 'frequency': 'daily', 'formula': '(Nombre d\'Accidents avec Arrêt / Nombre d\'Heures Travaillées) × 1000000', 'description': 'Mesurer la fréquence des accidents avec arrêt.'},
{'id': 2, 'category_id': 1, 'name': 'Nombre d\'Incidents/Near Miss', 'unit': 'incidents', 'target': 8, 'frequency': 'daily', 'formula': 'Compte des rapports d\'incidents', 'description': 'Évaluer la culture de sécurité.'},
{'id': 3, 'category_id': 1, 'name': 'Taux de Conformité aux Audits', 'unit': '%', 'target': 95, 'frequency': 'weekly', 'formula': '(Points Conformes / Total Points) × 100', 'description': 'Mesurer le respect des procédures.'},
# QUALITÉ (toutes les 10 minutes)
{'id': 4, 'category_id': 2, 'name': 'Taux de Rebut (Scrap Rate)', 'unit': '%', 'target': 1.5, 'frequency': 'per_10min', 'formula': '(Unités Rebutées / Unités Produites) × 100', 'description': 'Mesurer le % d\'unités jetées.'},
{'id': 5, 'category_id': 2, 'name': 'Taux de Retouche (Rework Rate)', 'unit': '%', 'target': 2.0, 'frequency': 'per_10min', 'formula': '(Unités Retouchées / Unités Totales) × 100', 'description': 'Mesurer le % d\'unités retouchées.'},
{'id': 6, 'category_id': 2, 'name': 'Nombre de Défauts par Unité (DPU)', 'unit': 'défauts/unité', 'target': 0.5, 'frequency': 'per_10min', 'formula': 'Défauts Totaux / Unités Inspectées', 'description': 'Mesurer le nombre moyen de défauts.'},
{'id': 7, 'category_id': 2, 'name': 'Taux de Retours Clients', 'unit': '%', 'target': 0.8, 'frequency': 'daily', 'formula': '(Unités Retournées / Unités Vendues) × 100', 'description': 'Mesurer l\'impact de la non-qualité.'},
{'id': 8, 'category_id': 2, 'name': 'Taux de rendement synthétique (TRS)', 'unit': '%', 'target': 85, 'frequency': 'per_30min', 'formula': 'Pièces bonnes × Temps cycle / Temps ouverture', 'description': 'Rendement global de la ligne.'},
{'id': 9, 'category_id': 2, 'name': 'Efficacité Globale de l\'Équipement (OEE)', 'unit': '%', 'target': 80, 'frequency': 'per_30min', 'formula': 'Disponibilité × Performance × Qualité', 'description': 'Mesurer l\'efficacité combinée.'},
# DÉLAIS (toutes les heures)
{'id': 10, 'category_id': 3, 'name': 'Taux de Respect du Plan', 'unit': '%', 'target': 95, 'frequency': 'hourly', 'formula': '(Produite / Planifiée) × 100', 'description': 'Mesurer la capacité à atteindre le volume.'},
{'id': 11, 'category_id': 3, 'name': 'Temps de Cycle (Cycle Time)', 'unit': 'min/unité', 'target': 50, 'frequency': 'per_10min', 'formula': 'Temps Total / Nombre d\'Unités', 'description': 'Mesurer le temps par unité.'},
{'id': 12, 'category_id': 3, 'name': 'Tack Time', 'unit': 'min/unité', 'target': 50, 'frequency': 'per_10min', 'formula': 'Temps production / Pièces demandées', 'description': 'Temps de production requis par unité.'},
{'id': 13, 'category_id': 3, 'name': 'Temps d\'Arrêt Imprévu (Downtime)', 'unit': 'h/jour', 'target': 1.5, 'frequency': 'daily', 'formula': 'Somme des Arrêts Non Planifiés', 'description': 'Mesurer l\'arrêt non planifié.'},
# COÛTS (quotidiennement)
{'id': 14, 'category_id': 4, 'name': 'Coût par Unité (CPU)', 'unit': '', 'target': 240, 'frequency': 'daily', 'formula': 'Coût Total / Unités Produites', 'description': 'Mesurer l\'efficacité des coûts.'},
{'id': 15, 'category_id': 4, 'name': 'Productivité de la Main-d\'œuvre', 'unit': 'unités/h', 'target': 8.0, 'frequency': 'hourly', 'formula': 'Unités Produites / Heures Main-d\'œuvre', 'description': 'Mesurer l\'efficacité de l\'équipe.'},
{'id': 16, 'category_id': 4, 'name': 'Coût des Non-Qualité (CNQ)', 'unit': '', 'target': 10000, 'frequency': 'daily', 'formula': 'Rebuts + Retouches + Retours', 'description': 'Mesurer le coût des défauts.'},
# MAINTENANCE (tous les 3 jours)
{'id': 17, 'category_id': 5, 'name': 'Temps Moyen Entre Pannes (MTBF)', 'unit': 'heures', 'target': 400, 'frequency': 'per_3days', 'formula': 'Temps Fonctionnement / Pannes', 'description': 'Mesurer la fiabilité.'},
{'id': 18, 'category_id': 5, 'name': 'Temps Moyen de Réparation (MTTR)', 'unit': 'heures', 'target': 2.5, 'frequency': 'per_3days', 'formula': 'Temps Réparation / Pannes', 'description': 'Mesurer la rapidité.'},
{'id': 19, 'category_id': 5, 'name': 'Ratio Maintenance Préventive/Corrective', 'unit': '%', 'target': 70, 'frequency': 'weekly', 'formula': 'Heures MP / (MP + MC)', 'description': 'Évaluer la stratégie.'},
{'id': 20, 'category_id': 5, 'name': 'Taux d\'Achèvement du Plan Préventif', 'unit': '%', 'target': 95, 'frequency': 'weekly', 'formula': '(Tâches Terminées / Tâches Planifiées) × 100', 'description': 'Mesurer le respect du plan.'},
{'id': 21, 'category_id': 5, 'name': 'Coût de Maintenance par Unité Produite', 'unit': '', 'target': 30, 'frequency': 'daily', 'formula': 'Coûts Maintenance / Unités Produites', 'description': 'Relier dépenses à production.'}
]
def get_frequency_minutes(frequency):
"""Retourne le nombre de minutes entre les mesures"""
frequencies = {
'per_10min': 10,
'per_30min': 30,
'hourly': 60,
'daily': 1440, # 24h
'weekly': 10080, # 7 jours
'per_3days': 4320 # 3 jours
}
return frequencies.get(frequency, 1440)
def generate_value(kpi_id, target, variance_range=0.2):
"""Génère une valeur réaliste autour de la cible"""
variance = (random.random() - 0.5) * 2 * variance_range
value = target * (1 + variance)
# Ajouter du bruit réaliste pour certains KPI
if kpi_id in [4, 5, 6, 11, 12]: # Qualité et délais - plus de variabilité
noise = (random.random() - 0.5) * 0.3 * target
value += noise
return max(0, round(value, 2))
def determine_status(kpi_id, value, target):
"""Détermine le statut (good, warning, critical)"""
tolerance = abs(target * 0.1)
# KPI où plus bas est mieux
if kpi_id in [2, 4, 5, 6, 13, 16]:
if value > target + tolerance * 2:
return 'critical'
elif value > target + tolerance:
return 'warning'
else:
# KPI où plus haut est mieux
if value < target - tolerance * 2:
return 'critical'
elif value < target - tolerance:
return 'warning'
return 'good'
def populate_database():
"""Remplit la base de données sur 1 an"""
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
# Créer les tables d'abord
print("🔄 Création du schéma...")
cursor.execute('''
CREATE TABLE IF NOT EXISTS categories (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
emoji TEXT,
description TEXT
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS kpis (
id INTEGER PRIMARY KEY,
category_id INTEGER NOT NULL,
name TEXT NOT NULL,
unit TEXT,
target REAL,
frequency TEXT,
formula TEXT,
description TEXT,
FOREIGN KEY(category_id) REFERENCES categories(id)
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS measurements (
id INTEGER PRIMARY KEY AUTOINCREMENT,
kpi_id INTEGER NOT NULL,
measurement_date DATETIME NOT NULL,
value REAL NOT NULL,
status TEXT,
FOREIGN KEY(kpi_id) REFERENCES kpis(id)
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS alerts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
kpi_id INTEGER NOT NULL,
alert_type TEXT,
severity TEXT,
message TEXT,
created_at DATETIME,
FOREIGN KEY(kpi_id) REFERENCES kpis(id)
)
''')
# Supprimer les anciennes données
cursor.execute('DELETE FROM measurements')
cursor.execute('DELETE FROM alerts')
cursor.execute('DELETE FROM kpis')
cursor.execute('DELETE FROM categories')
# Insérer les catégories
for cat in CATEGORIES:
cursor.execute(
'INSERT INTO categories (id, name, emoji, description) VALUES (?, ?, ?, ?)',
(cat['id'], cat['name'], cat['emoji'], cat['description'])
)
# Insérer les KPI
for kpi in KPIS:
cursor.execute(
'INSERT INTO kpis (id, category_id, name, unit, target, frequency, formula, description) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
(kpi['id'], kpi['category_id'], kpi['name'], kpi['unit'], kpi['target'], kpi['frequency'], kpi['formula'], kpi['description'])
)
# Générer les mesures sur 1 an
end_date = datetime.now()
start_date = end_date - timedelta(days=365)
print("🔄 Génération des mesures sur 1 an...")
measurements_count = 0
for kpi in KPIS:
kpi_id = kpi['id']
target = kpi['target']
frequency_minutes = get_frequency_minutes(kpi['frequency'])
current_date = start_date
while current_date <= end_date:
value = generate_value(kpi_id, target)
status = determine_status(kpi_id, value, target)
cursor.execute(
'INSERT INTO measurements (kpi_id, measurement_date, value, status) VALUES (?, ?, ?, ?)',
(kpi_id, current_date.isoformat(), value, status)
)
measurements_count += 1
current_date += timedelta(minutes=frequency_minutes)
conn.commit()
conn.close()
print(f"✅ Base de données remplie avec succès!")
print(f"📊 {measurements_count} mesures créées")
print(f"🎯 {len(KPIS)} KPI configurés")
print(f"📁 Fichier: {DB_PATH}")
if __name__ == '__main__':
populate_database()