75 KiB
Executable File
MODULE PÉRIPHÉRIQUES - Spécifications Complètes
Linux BenchTools - Extension Inventaire Matériel
Version: 1.0 Date: 2025-12-30 Auteur: Analyse collaborative Projet: serv_benchmark - Module Périphériques
📋 TABLE DES MATIÈRES
- Vue d'ensemble
- Objectifs et Portée
- Types de Périphériques
- Caractéristiques des Données
- Architecture Base de Données
- Architecture Backend
- Architecture Frontend
- Fonctionnalités Principales
- Système de Localisation
- Système de Prêt
- Appareils Complets
- Configuration YAML
- Plan de Déploiement
- Prompt de Développement
🎯 VUE D'ENSEMBLE
Contexte
Le projet Linux BenchTools est une application web existante permettant de :
- Benchmarker des machines Linux (CPU, RAM, Disque, Réseau, GPU)
- Stocker l'historique des benchmarks
- Gérer un inventaire de machines (devices)
Besoin
Ajouter un module complet de gestion d'inventaire de périphériques pour :
- Cataloguer tous les périphériques informatiques (USB, Bluetooth, PCIe, câbles, etc.)
- Gérer leur localisation physique (pièces, placards, tiroirs)
- Suivre les prêts/emprunts
- Lier certains périphériques aux machines benchmarkées
- Gérer stock, photos, documents, garanties
Architecture Existante
Backend:
- FastAPI + SQLAlchemy ORM
- Base de données: SQLite (
data.db) - Structure:
/backend/app/avec models/, schemas/, api/, core/ - Authentification par token
Frontend:
- Vanilla JavaScript (pas de framework)
- Thème: Monokai dark
- Layout: Two-panel design
- Pages: Dashboard, Devices, Settings
Base de données existante (data.db):
devices- Machines inventoriéeshardware_snapshots- Snapshots matérielbenchmarks- Résultats benchmarksdocuments- Documents devicesmanufacturer_links- Liens
🎯 OBJECTIFS ET PORTÉE
Objectifs Principaux
-
Inventaire Complet
- Cataloguer TOUS les périphériques informatiques possibles
- USB, Bluetooth, Wi-Fi, PCIe, HDMI, Câbles, Visserie, etc.
- Appareils complets (Laptops, Desktops, Smartphones, Tablettes, Serveurs, Consoles)
-
Gestion Physique
- Localisation précise (Pièce > Placard > Tiroir > Étagère)
- Tracking des déplacements
- QR codes pour identification rapide
-
Gestion de Prêt
- Prêter périphériques à des personnes
- Suivre les retours
- Gérer cautions
- Alertes et rappels automatiques
-
Intégration Benchmarks
- Lier appareils complets aux devices benchmarkés
- Afficher historique benchmarks
- Sync automatique des specs
-
Gestion Avancée
- Stock et quantités
- Photos et documents
- Évaluation qualité (0-5 étoiles)
- Configuration YAML flexible
- Compression d'images automatique
Architecture
BASE DE DONNÉES SÉPARÉE: peripherals.db
- Isolation des données
- Performance optimale
- Backups indépendants
- Module activable/désactivable
📦 TYPES DE PÉRIPHÉRIQUES
1. CONNECTIVITÉ FILAIRE
USB (Universal Serial Bus)
- Types: USB Type-A (2.0, 3.0, 3.1, 3.2), USB Type-C, Thunderbolt 3/4, Mini/Micro
- Périphériques:
- Clés USB / Disques externes
- Souris / Claviers
- Webcams
- Imprimantes / Scanners
- Adaptateurs réseau (Ethernet, Wi-Fi)
- Cartes son externes / DAC
- Dongles (Bluetooth, Wi-Fi, DVB-T)
- Lecteurs de cartes
- Contrôleurs de jeux
- Microphones / Interfaces audio
- Hubs USB
PCIe (Peripheral Component Interconnect Express)
- Cartes graphiques (GPU)
- Cartes son
- Cartes réseau (Ethernet, Wi-Fi, Fibre)
- Cartes d'acquisition vidéo
- Contrôleurs RAID
- Adaptateurs NVMe
- Cartes USB/Thunderbolt d'extension
- Cartes TV/DVB
SATA / M.2 / NVMe
- Disques durs (HDD)
- SSD SATA
- SSD M.2 (SATA/NVMe)
- Lecteurs optiques (CD/DVD/Blu-ray)
Audio / Vidéo
- Audio: Jack 3.5mm/6.35mm, XLR, Optique S/PDIF, Coaxial
- Vidéo: HDMI (1.4, 2.0, 2.1), DisplayPort, DVI, VGA, Mini DisplayPort
- Périphériques: Écrans, Projecteurs, Casques, Enceintes, Microphones
2. CONNECTIVITÉ SANS-FIL
Wi-Fi
- Normes: 802.11 a/b/g/n/ac/ax (Wi-Fi 4/5/6/6E/7)
- Bandes: 2.4 GHz, 5 GHz, 6 GHz
- Adaptateurs USB, Cartes PCIe/M.2, Points d'accès
Bluetooth
- Versions: 2.0 à 5.3
- Profils: A2DP, HFP, HSP, HID
- Casques, Claviers, Souris, Enceintes, Adaptateurs
Autres
- NFC, RF propriétaire, Infrarouge
3. RÉSEAU
Ethernet
- Vitesses: 10/100/1000 Mbps, 2.5G, 5G, 10G, 25G, 40G
- Cartes NIC, Switches, Routeurs, Modems
Fibre Optique
- Cartes SFP/SFP+, Convertisseurs
4. CÂBLES
Types de Câbles
- HDMI: Versions 1.4, 2.0, 2.1 (Standard, Premium, Ultra)
- DisplayPort: Versions 1.2, 1.4, 2.0
- USB: USB-A, USB-C, Mini, Micro (différentes longueurs)
- Ethernet: Cat5e, Cat6, Cat6a, Cat7, Cat8 (blindé/non-blindé)
- Audio: Jack 3.5mm, XLR, RCA, Optique
- Alimentation: C13, C14, Secteur, USB-C PD
- Vidéo: DVI, VGA, Composite
Caractéristiques spécifiques:
- Longueur (mètres)
- Certification (pour HDMI)
- Blindage (pour Ethernet)
- Puissance supportée (pour USB-C)
5. VISSERIE ET FIXATIONS
Types
- Vis: M2, M3, M4 (différentes longueurs, têtes: plate, fraisée, bombée)
- Entretoises (Standoffs): M2, M3, M4 (différentes hauteurs)
- Écrous, Rondelles
- Clips plastiques, Attaches câbles
Caractéristiques:
- Diamètre/Norme
- Longueur
- Matériau (Acier, Inox, Laiton, Plastique)
- Type de tête
6. BOÎTIERS PC
Formats
- Mini-ITX
- Micro-ATX
- ATX
- E-ATX
- Full Tower
Caractéristiques:
- Emplacements 3.5" / 2.5"
- Ventilateurs inclus/supportés
- Fenêtre latérale
- RGB
- Dimensions
7. STOCKAGE
- Disques durs externes
- SSD externes
- NAS (Network Attached Storage)
- Lecteurs de bandes
- Stations d'accueil
8. ENTRÉE / SORTIE
Entrée
- Claviers (USB, Bluetooth, PS/2)
- Souris / Trackballs / Tablettes graphiques
- Scanners
- Lecteurs de codes-barres
- Lecteurs biométriques
- Webcams
- Microphones
Sortie
- Imprimantes (USB, réseau, sans-fil)
- Imprimantes 3D
- Enceintes
- Projecteurs
9. APPAREILS COMPLETS
Desktop (PC de Bureau)
Caractéristiques:
- CPU (marque, modèle, cores, threads, fréquence)
- RAM (capacité, type DDR, fréquence)
- GPU (intégré/dédié, modèle, VRAM)
- Stockage (type, capacité)
- Carte mère (marque, modèle, chipset)
- Alimentation (wattage, certification 80+)
- Boîtier (format, modèle)
- Refroidissement (Air/AIO/Watercooling)
- Système d'exploitation
Laptop (Ordinateur Portable)
Caractéristiques:
- Écran (taille, résolution, tactile, taux rafraîchissement)
- CPU (marque, modèle, génération)
- RAM (capacité, extensible, max)
- Stockage (type, capacité, slots M.2 libres)
- GPU (intégré/dédié/hybrid)
- Batterie (Wh, autonomie)
- Connectique (USB-A, USB-C, Thunderbolt, HDMI, Ethernet, SD)
- Webcam (résolution)
- Poids
Tablette
Caractéristiques:
- OS (iOS/iPadOS, Android, Windows)
- Écran (taille, résolution, type: LCD/OLED)
- Processeur
- RAM, Stockage
- Batterie (mAh)
- Stylet (inclus, modèle)
- Clavier (inclus)
- 4G/5G
- Poids
Smartphone
Caractéristiques:
- OS (iOS, Android)
- Écran (taille, résolution, type, fréquence Hz)
- Processeur
- RAM, Stockage
- Caméras (principale MP, ultra-wide, téléphoto, frontale)
- Batterie (mAh, charge rapide W, sans-fil)
- 5G, Dual SIM, eSIM
- Résistance (IP67/IP68)
- Lecteur d'empreintes
- IMEI
Serveur
Caractéristiques:
- Format (Tour, Rack 1U/2U/4U, Blade)
- OS (Windows Server, Linux, ESXi, Proxmox)
- CPU (nombre, modèle, cores total)
- RAM (capacité, type ECC)
- RAID (contrôleur, niveau)
- Baies (3.5", 2.5")
- Alimentation (redondante, wattage)
- IPMI/iLO/iDRAC
Console de Jeu
Caractéristiques:
- Type: Console de salon, Portable, Hybrid
- Marque: PlayStation, Xbox, Nintendo, Steam, Autre
- Modèle: PS5, Xbox Series X, Switch OLED, Steam Deck, etc.
- Génération: PS5 (9ème gen), PS4 (8ème gen), etc.
- Stockage (capacité, type, extensible)
- Résolution max (1080p, 4K, 8K)
- FPS max
- Ray tracing
- VR compatible
- Lecteur disque physique
- Rétrocompatibilité
- Manettes incluses
- Connectique (HDMI version, USB, Ethernet)
- Dimensions
- Poids
Exemple de configuration YAML:
- id: console_gaming
nom: "Console de Jeu"
icon: "gamepad"
is_complete_device: true
champs_specifiques:
- type_console:
type: "select"
options: ["Salon", "Portable", "Hybrid"]
label: "Type de console"
- marque:
type: "select"
options: ["PlayStation", "Xbox", "Nintendo", "Steam", "Autre"]
- modele:
type: "text"
placeholder: "ex: PlayStation 5, Xbox Series X, Switch OLED"
- generation:
type: "text"
label: "Génération"
- stockage_go:
type: "number"
label: "Stockage (Go)"
- stockage_type:
type: "select"
options: ["SSD", "HDD", "Cartouche"]
- stockage_extensible:
type: "boolean"
- resolution_max:
type: "select"
options: ["720p", "1080p", "1440p", "4K", "8K"]
- fps_max:
type: "number"
label: "FPS maximum"
- ray_tracing:
type: "boolean"
- vr_compatible:
type: "boolean"
label: "Compatible VR"
- lecteur_disque:
type: "boolean"
label: "Lecteur disque physique"
- retrocompatibilite:
type: "text"
label: "Rétrocompatibilité"
placeholder: "ex: PS4, Xbox One"
- manettes_incluses:
type: "number"
label: "Manettes incluses"
- hdmi_version:
type: "select"
options: ["1.4", "2.0", "2.1"]
- ports_usb:
type: "number"
- ethernet_gbps:
type: "select"
options: ["1", "2.5", "10"]
- wifi:
type: "select"
options: ["Wi-Fi 5", "Wi-Fi 6", "Wi-Fi 6E"]
- bluetooth:
type: "select"
options: ["4.0", "5.0", "5.1", "5.2"]
- poids_kg:
type: "number"
step: 0.1
10. AUTRES
- Onduleurs (UPS)
- Alimentations PC
- Cartes d'acquisition (DAQ)
- Dispositifs KVM
- Contrôleurs MIDI
- GPS USB
- Dongles de sécurité
🔧 CARACTÉRISTIQUES DES DONNÉES
Caractéristiques GLOBALES (tous périphériques)
📝 IDENTIFICATION
├── id (auto-increment)
├── nom (user-defined name)
├── type_principal (USB, Bluetooth, PCIe, etc.)
├── sous_type (Clé USB, Souris, GPU, etc.)
├── marque (manufacturer)
├── modele (model number)
├── numero_serie (serial number)
└── ean_upc (barcode)
💰 ACHAT
├── boutique (store name)
├── date_achat (purchase date)
├── prix (price)
├── devise (currency, default: EUR)
├── garantie_duree_mois (warranty duration)
└── garantie_expiration (warranty expiration date)
⭐ ÉVALUATION
├── rating (0-5 étoiles, float)
└── notes (free text review)
📦 STOCK
├── quantite_totale (total quantity)
├── quantite_disponible (available)
├── seuil_alerte (alert threshold)
└── emplacement_stockage (storage location)
📷 MÉDIAS
├── photos[] (array of image paths)
├── pdf_manuels[] (manuals)
├── pdf_factures[] (invoices)
└── pdf_autres[] (other docs)
🔗 LIENS & RÉFÉRENCES
├── url_fabricant (manufacturer page)
├── url_support (support page)
├── url_drivers (drivers download)
├── url_documentation
└── liens_personnalises[] (custom links)
📋 MÉTADONNÉES
├── date_creation (record creation)
├── date_modification (last update)
├── etat (Neuf, Bon, Usagé, Défectueux, Retiré)
├── localisation (physical location)
├── proprietaire (owner)
├── tags[] (custom tags, JSON)
└── notes (free text)
🐧 IDENTIFICATION LINUX
├── device_path (e.g., /dev/sda, /dev/input/mouse0)
├── sysfs_path (e.g., /sys/devices/pci0000:00/...)
├── vendor_id (USB: VID, PCI: vendor ID)
├── product_id (USB: PID, PCI: device ID)
├── class_id (device class)
├── driver_utilise (kernel driver in use)
├── modules_kernel[] (required kernel modules, JSON)
├── udev_rules (custom udev rules)
└── identifiant_systeme (lsusb, lspci output)
⚙️ INSTALLATION & PROBLÈMES
├── installation_auto (auto-detected: bool)
├── driver_requis (required drivers)
├── firmware_requis (required firmware)
├── paquets_necessaires[] (required packages, JSON)
├── commandes_installation (installation commands)
├── problemes_connus (known issues)
├── solutions (solutions/workarounds)
└── compatibilite_noyau (kernel version compatibility)
🔌 CONNECTIVITÉ
├── interface_connexion (USB, PCIe, Bluetooth, etc.)
├── connecte_a (connected to which device/port)
└── consommation_electrique_w (power consumption)
📍 LOCALISATION PHYSIQUE
├── location_id (FK vers locations)
├── location_details (détails supplémentaires)
└── location_auto (bool: suit le device assigné)
🤝 PRÊT / EMPRUNT
├── en_pret (bool)
├── pret_actuel_id (FK vers peripheral_loans)
└── prete_a (nom de l'emprunteur, pour affichage rapide)
💻 APPAREIL COMPLET
├── is_complete_device (bool)
├── device_type (desktop, laptop, tablet, smartphone, server, console)
├── linked_device_id (→ devices.id dans data.db, pour benchmarks)
└── device_id (→ devices.id dans data.db, assignation actuelle)
🎨 DONNÉES SPÉCIFIQUES
└── caracteristiques_specifiques (JSON flexible par type)
Caractéristiques SPÉCIFIQUES (exemples par type)
Stockage USB
{
"capacite_go": 128,
"type_memoire": "Flash NAND",
"usb_version": "3.2 Gen 1",
"vitesse_lecture_mbs": 150,
"vitesse_ecriture_mbs": 50,
"systeme_fichiers": "exFAT",
"chiffrement": true,
"chiffrement_type": "AES 256-bit"
}
Souris
{
"connexion": "Bluetooth 5.0",
"capteur_type": "Optique",
"dpi_max": 16000,
"dpi_reglable": true,
"nombre_boutons": 8,
"boutons_programmables": 6,
"autonomie_heures": 200,
"type_batterie": "Rechargeable Li-ion",
"eclairage_rgb": true
}
Câble HDMI
{
"longueur_m": 2,
"version_hdmi": "2.1",
"certification": "Premium",
"debit_max_gbps": 48,
"support_4k_120hz": true,
"support_8k": true,
"arc_earc": "eARC"
}
Desktop PC
{
"systeme_exploitation": "Windows 11 Pro",
"cpu_marque": "Intel",
"cpu_modele": "Core i7-12700K",
"cpu_cores": 12,
"cpu_threads": 20,
"ram_total_go": 32,
"ram_type": "DDR5",
"ram_freq_mhz": 5600,
"gpu_type": "Dédié",
"gpu_modele": "NVIDIA RTX 4070",
"gpu_vram_go": 12,
"stockage_principal": "1TB NVMe SSD",
"carte_mere_marque": "ASUS",
"carte_mere_modele": "ROG STRIX Z690-F",
"alimentation_w": 850,
"alimentation_certification": "80+ Gold",
"boitier_format": "ATX",
"refroidissement": "AIO 280mm"
}
Console de Jeu
{
"type_console": "Salon",
"marque": "PlayStation",
"modele": "PlayStation 5",
"generation": "9ème génération",
"stockage_go": 825,
"stockage_type": "SSD",
"stockage_extensible": true,
"resolution_max": "4K",
"fps_max": 120,
"ray_tracing": true,
"vr_compatible": true,
"lecteur_disque": true,
"retrocompatibilite": "PS4",
"manettes_incluses": 1,
"hdmi_version": "2.1",
"ports_usb": 4,
"ethernet_gbps": "1",
"wifi": "Wi-Fi 6",
"bluetooth": "5.1",
"poids_kg": 4.5
}
🗄️ ARCHITECTURE BASE DE DONNÉES
Stratégie: Deux Bases de Données Séparées
/backend/data/
├── data.db # DB PRINCIPALE (existante)
│ ├── devices # Machines benchmarkées
│ ├── hardware_snapshots # Snapshots hardware
│ ├── benchmarks # Résultats benchmarks
│ ├── documents # Documents devices
│ ├── manufacturer_links # Liens devices
│ └── disk_smart # Données SMART
│
└── peripherals.db # DB PÉRIPHÉRIQUES (nouvelle)
├── peripherals # Périphériques
├── peripheral_photos # Photos
├── peripheral_documents # Documents
├── peripheral_links # Liens
├── peripheral_loans # Prêts
├── peripheral_location_history # Historique déplacements
├── peripheral_attachments # Relations périph ↔ périph
├── locations # Emplacements physiques
└── borrower_blacklist # Liste noire emprunteurs
Pourquoi Séparé ?
✅ Isolation - Données indépendantes ✅ Performance - Pas de contention ✅ Scalabilité - Peut grossir indépendamment ✅ Backups - Stratégies distinctes ✅ Migration - Plus facile ✅ Modularité - Activable/désactivable
Schéma Table peripherals
CREATE TABLE peripherals (
-- IDENTIFICATION
id INTEGER PRIMARY KEY AUTOINCREMENT,
nom VARCHAR(255) NOT NULL,
type_principal VARCHAR(100) NOT NULL,
sous_type VARCHAR(100),
marque VARCHAR(100),
modele VARCHAR(255),
numero_serie VARCHAR(255),
ean_upc VARCHAR(50),
-- ACHAT
boutique VARCHAR(255),
date_achat DATE,
prix DECIMAL(10, 2),
devise VARCHAR(10) DEFAULT 'EUR',
garantie_duree_mois INTEGER,
garantie_expiration DATE,
-- ÉVALUATION
rating FLOAT DEFAULT 0, -- 0-5 étoiles
-- STOCK
quantite_totale INTEGER DEFAULT 1,
quantite_disponible INTEGER DEFAULT 1,
seuil_alerte INTEGER DEFAULT 0,
-- MÉTADONNÉES
date_creation TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
date_modification TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
etat VARCHAR(50) DEFAULT 'Neuf',
localisation VARCHAR(255),
proprietaire VARCHAR(100),
tags TEXT, -- JSON array
notes TEXT,
-- LINUX IDENTIFICATION
device_path VARCHAR(255),
sysfs_path VARCHAR(500),
vendor_id VARCHAR(20),
product_id VARCHAR(20),
class_id VARCHAR(20),
driver_utilise VARCHAR(100),
modules_kernel TEXT, -- JSON
udev_rules TEXT,
identifiant_systeme TEXT,
-- INSTALLATION
installation_auto BOOLEAN DEFAULT FALSE,
driver_requis TEXT,
firmware_requis TEXT,
paquets_necessaires TEXT, -- JSON
commandes_installation TEXT,
problemes_connus TEXT,
solutions TEXT,
compatibilite_noyau VARCHAR(100),
-- CONNECTIVITÉ
interface_connexion VARCHAR(100),
connecte_a VARCHAR(255),
consommation_electrique_w DECIMAL(6, 2),
-- LOCALISATION PHYSIQUE
location_id INTEGER,
location_details VARCHAR(500),
location_auto BOOLEAN DEFAULT TRUE,
-- PRÊT
en_pret BOOLEAN DEFAULT FALSE,
pret_actuel_id INTEGER,
prete_a VARCHAR(255),
-- APPAREIL COMPLET
is_complete_device BOOLEAN DEFAULT FALSE,
device_type VARCHAR(50),
-- LIEN VERS DB PRINCIPALE (logique, pas FK SQL)
linked_device_id INTEGER, -- → devices.id dans data.db (benchmarks)
device_id INTEGER, -- → devices.id dans data.db (assignation)
-- DONNÉES SPÉCIFIQUES
caracteristiques_specifiques TEXT -- JSON
);
CREATE INDEX idx_peripherals_type ON peripherals(type_principal);
CREATE INDEX idx_peripherals_sous_type ON peripherals(sous_type);
CREATE INDEX idx_peripherals_marque ON peripherals(marque);
CREATE INDEX idx_peripherals_device ON peripherals(device_id);
CREATE INDEX idx_peripherals_etat ON peripherals(etat);
CREATE INDEX idx_peripherals_complete_device ON peripherals(is_complete_device);
CREATE INDEX idx_peripherals_en_pret ON peripherals(en_pret);
Table peripheral_photos
CREATE TABLE peripheral_photos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
peripheral_id INTEGER NOT NULL,
filename VARCHAR(255) NOT NULL,
stored_path VARCHAR(500) NOT NULL,
mime_type VARCHAR(100),
size_bytes INTEGER,
uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
description TEXT,
is_primary BOOLEAN DEFAULT FALSE,
FOREIGN KEY (peripheral_id) REFERENCES peripherals(id) ON DELETE CASCADE
);
CREATE INDEX idx_peripheral_photos_pid ON peripheral_photos(peripheral_id);
Table peripheral_documents
CREATE TABLE peripheral_documents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
peripheral_id INTEGER NOT NULL,
doc_type VARCHAR(50) NOT NULL, -- manual, warranty, invoice, datasheet, other
filename VARCHAR(255) NOT NULL,
stored_path VARCHAR(500) NOT NULL,
mime_type VARCHAR(100),
size_bytes INTEGER,
uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
description TEXT,
FOREIGN KEY (peripheral_id) REFERENCES peripherals(id) ON DELETE CASCADE
);
CREATE INDEX idx_peripheral_documents_pid ON peripheral_documents(peripheral_id);
CREATE INDEX idx_peripheral_documents_type ON peripheral_documents(doc_type);
Table peripheral_links
CREATE TABLE peripheral_links (
id INTEGER PRIMARY KEY AUTOINCREMENT,
peripheral_id INTEGER NOT NULL,
link_type VARCHAR(50) NOT NULL, -- manufacturer, support, drivers, documentation, custom
label VARCHAR(255) NOT NULL,
url TEXT NOT NULL,
FOREIGN KEY (peripheral_id) REFERENCES peripherals(id) ON DELETE CASCADE
);
CREATE INDEX idx_peripheral_links_pid ON peripheral_links(peripheral_id);
Table peripheral_loans
CREATE TABLE peripheral_loans (
id INTEGER PRIMARY KEY AUTOINCREMENT,
peripheral_id INTEGER NOT NULL,
-- Emprunteur
emprunte_par VARCHAR(255) NOT NULL,
email_emprunteur VARCHAR(255),
telephone VARCHAR(50),
-- Dates
date_pret DATE NOT NULL,
date_retour_prevue DATE NOT NULL,
date_retour_effectif DATE,
-- Statut
statut VARCHAR(50) NOT NULL DEFAULT 'en_cours', -- en_cours, retourne, en_retard
-- Caution
caution_montant DECIMAL(10, 2),
caution_rendue BOOLEAN DEFAULT FALSE,
-- État
etat_depart VARCHAR(50),
etat_retour VARCHAR(50),
problemes_retour TEXT,
-- Informations
raison_pret TEXT,
notes TEXT,
created_by VARCHAR(100),
-- Rappels
rappel_envoye BOOLEAN DEFAULT FALSE,
date_rappel TIMESTAMP,
FOREIGN KEY (peripheral_id) REFERENCES peripherals(id) ON DELETE CASCADE
);
CREATE INDEX idx_loans_peripheral ON peripheral_loans(peripheral_id);
CREATE INDEX idx_loans_statut ON peripheral_loans(statut);
CREATE INDEX idx_loans_emprunteur ON peripheral_loans(emprunte_par);
CREATE INDEX idx_loans_retour_prevue ON peripheral_loans(date_retour_prevue);
Table locations
CREATE TABLE locations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nom VARCHAR(255) NOT NULL UNIQUE,
type VARCHAR(50) NOT NULL, -- piece, placard, tiroir, etagere, meuble, boite
parent_id INTEGER, -- Hiérarchie
description TEXT,
image_path VARCHAR(500),
qr_code_path VARCHAR(500),
ordre_affichage INTEGER DEFAULT 0,
FOREIGN KEY (parent_id) REFERENCES locations(id) ON DELETE CASCADE
);
CREATE INDEX idx_locations_parent ON locations(parent_id);
CREATE INDEX idx_locations_type ON locations(type);
Table peripheral_location_history
CREATE TABLE peripheral_location_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
peripheral_id INTEGER NOT NULL,
from_location_id INTEGER,
to_location_id INTEGER,
from_device_id INTEGER,
to_device_id INTEGER,
action VARCHAR(50) NOT NULL, -- moved, assigned, unassigned, stored
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
notes TEXT,
user VARCHAR(100),
FOREIGN KEY (peripheral_id) REFERENCES peripherals(id) ON DELETE CASCADE,
FOREIGN KEY (from_location_id) REFERENCES locations(id) ON DELETE SET NULL,
FOREIGN KEY (to_location_id) REFERENCES locations(id) ON DELETE SET NULL
);
CREATE INDEX idx_peripheral_history_pid ON peripheral_location_history(peripheral_id);
Organisation Fichiers Uploads
/uploads/peripherals/
├── photos/
│ ├── {hash16}_{peripheral_id}_1.jpg
│ ├── {hash16}_{peripheral_id}_2.png
│ └── ...
├── documents/
│ ├── manuals/
│ │ └── {hash16}_{peripheral_id}_manual.pdf
│ ├── invoices/
│ │ └── {hash16}_{peripheral_id}_invoice.pdf
│ ├── warranties/
│ │ └── {hash16}_{peripheral_id}_warranty.pdf
│ └── datasheets/
│ └── {hash16}_{peripheral_id}_datasheet.pdf
└── thumbnails/
└── {hash16}_{peripheral_id}_thumb.webp
⚙️ ARCHITECTURE BACKEND
Configuration (/backend/app/core/config.py)
class Settings(BaseSettings):
# ... config existante ...
# DATABASE PRINCIPALE (benchmarks)
DATABASE_URL: str = os.getenv(
"DATABASE_URL",
"sqlite:///./backend/data/data.db"
)
# DATABASE PÉRIPHÉRIQUES (nouvelle)
PERIPHERALS_DB_URL: str = os.getenv(
"PERIPHERALS_DB_URL",
"sqlite:///./backend/data/peripherals.db"
)
# Upload directories
UPLOAD_DIR: str = "./uploads"
PERIPHERALS_UPLOAD_DIR: str = "./uploads/peripherals"
# Module peripherals enabled/disabled
PERIPHERALS_MODULE_ENABLED: bool = os.getenv(
"PERIPHERALS_MODULE_ENABLED",
"true"
).lower() == "true"
# Image compression
IMAGE_COMPRESSION_ENABLED: bool = True
IMAGE_COMPRESSION_QUALITY: int = 85
IMAGE_MAX_WIDTH: int = 1920
IMAGE_MAX_HEIGHT: int = 1080
THUMBNAIL_SIZE: int = 300
THUMBNAIL_QUALITY: int = 75
THUMBNAIL_FORMAT: str = "webp"
Sessions DB (/backend/app/db/session.py)
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session
# DB Principale
engine_main = create_engine(settings.DATABASE_URL, ...)
SessionLocalMain = sessionmaker(bind=engine_main)
# DB Périphériques
engine_peripherals = create_engine(settings.PERIPHERALS_DB_URL, ...)
SessionLocalPeripherals = sessionmaker(bind=engine_peripherals)
# Dependency Injection
def get_db() -> Session:
"""Session DB principale"""
db = SessionLocalMain()
try:
yield db
finally:
db.close()
def get_peripherals_db() -> Session:
"""Session DB périphériques"""
db = SessionLocalPeripherals()
try:
yield db
finally:
db.close()
Modèles SQLAlchemy
Base:
# /backend/app/db/base.py
Base = declarative_base() # DB principale
BasePeripherals = declarative_base() # DB périphériques
Modèle Peripheral:
# /backend/app/models/peripheral.py
from app.db.base import BasePeripherals
class Peripheral(BasePeripherals):
__tablename__ = "peripherals"
# ... tous les champs définis plus haut
Schémas Pydantic
# /backend/app/schemas/peripheral.py
class PeripheralBase(BaseModel):
nom: str
type_principal: str
sous_type: Optional[str] = None
marque: Optional[str] = None
modele: Optional[str] = None
# ... tous les champs
class PeripheralCreate(PeripheralBase):
pass
class PeripheralUpdate(BaseModel):
nom: Optional[str] = None
# ... tous les champs optionnels
class PeripheralDetail(PeripheralBase):
id: int
date_creation: datetime
photos: List[PeripheralPhotoSchema]
documents: List[PeripheralDocumentSchema]
links: List[PeripheralLinkSchema]
loan: Optional[PeripheralLoanSchema]
location: Optional[LocationSchema]
class Config:
from_attributes = True
class PeripheralSummary(BaseModel):
id: int
nom: str
type_principal: str
sous_type: Optional[str]
marque: Optional[str]
etat: str
rating: float
prix: Optional[float]
photo_principale: Optional[str]
en_pret: bool
class Config:
from_attributes = True
class PeripheralListResponse(BaseModel):
items: List[PeripheralSummary]
total: int
page: int
page_size: int
total_pages: int
Routes API (/backend/app/api/peripherals.py)
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
from sqlalchemy.orm import Session
from app.db.session import get_db, get_peripherals_db
from app.services.peripheral_service import PeripheralService
router = APIRouter(prefix="/api/peripherals", tags=["peripherals"])
# CRUD Périphériques
@router.get("/", response_model=PeripheralListResponse)
async def list_peripherals(
page: int = 1,
page_size: int = 50,
search: Optional[str] = None,
type_principal: Optional[str] = None,
marque: Optional[str] = None,
etat: Optional[str] = None,
en_pret: Optional[bool] = None,
is_complete_device: Optional[bool] = None,
db: Session = Depends(get_peripherals_db)
):
"""Liste périphériques avec pagination et filtres"""
pass
@router.get("/{peripheral_id}", response_model=PeripheralDetail)
async def get_peripheral(
peripheral_id: int,
db_peripherals: Session = Depends(get_peripherals_db),
db_main: Session = Depends(get_db)
):
"""Détail périphérique avec relations"""
return PeripheralService.get_peripheral_with_device(
peripheral_id, db_peripherals, db_main
)
@router.post("/", response_model=PeripheralDetail)
async def create_peripheral(
peripheral: PeripheralCreate,
db: Session = Depends(get_peripherals_db)
):
"""Créer périphérique"""
pass
@router.put("/{peripheral_id}")
async def update_peripheral(
peripheral_id: int,
data: PeripheralUpdate,
db: Session = Depends(get_peripherals_db)
):
"""Mettre à jour périphérique"""
pass
@router.delete("/{peripheral_id}")
async def delete_peripheral(
peripheral_id: int,
db: Session = Depends(get_peripherals_db)
):
"""Supprimer périphérique"""
pass
# Photos
@router.post("/{peripheral_id}/photos")
async def upload_photo(
peripheral_id: int,
file: UploadFile = File(...),
compress: bool = True,
db: Session = Depends(get_peripherals_db)
):
"""Upload photo avec compression optionnelle"""
pass
@router.get("/{peripheral_id}/photos")
async def get_photos(
peripheral_id: int,
db: Session = Depends(get_peripherals_db)
):
"""Liste photos"""
pass
@router.delete("/photos/{photo_id}")
async def delete_photo(
photo_id: int,
db: Session = Depends(get_peripherals_db)
):
"""Supprimer photo"""
pass
# Documents
@router.post("/{peripheral_id}/documents")
async def upload_document(
peripheral_id: int,
file: UploadFile = File(...),
doc_type: str = "other",
db: Session = Depends(get_peripherals_db)
):
"""Upload document"""
pass
@router.get("/{peripheral_id}/documents")
async def get_documents(
peripheral_id: int,
db: Session = Depends(get_peripherals_db)
):
"""Liste documents"""
pass
@router.get("/documents/{doc_id}/download")
async def download_document(doc_id: int, db: Session = Depends(get_peripherals_db)):
"""Télécharger document"""
pass
# Liens
@router.post("/{peripheral_id}/links")
@router.get("/{peripheral_id}/links")
@router.put("/links/{link_id}")
@router.delete("/links/{link_id}")
# Prêts
@router.post("/{peripheral_id}/loan")
async def create_loan(
peripheral_id: int,
loan: LoanCreate,
db: Session = Depends(get_peripherals_db)
):
"""Créer un prêt"""
pass
@router.post("/loans/{loan_id}/return")
async def return_loan(
loan_id: int,
return_data: LoanReturn,
db: Session = Depends(get_peripherals_db)
):
"""Enregistrer retour"""
pass
@router.get("/loans/active")
@router.get("/loans/overdue")
@router.post("/loans/{loan_id}/reminder")
# Localisation
@router.post("/{peripheral_id}/assign")
@router.post("/{peripheral_id}/unassign")
@router.get("/{peripheral_id}/history")
# Appareils complets
@router.post("/{peripheral_id}/link-device")
@router.delete("/{peripheral_id}/unlink-device")
@router.get("/{peripheral_id}/benchmarks")
# Recherche avancée
@router.get("/search")
async def advanced_search(
q: str,
filters: dict,
db: Session = Depends(get_peripherals_db)
):
"""Recherche multi-critères"""
pass
# Config
@router.get("/types")
async def get_peripheral_types():
"""Liste types depuis YAML"""
pass
Service Layer (/backend/app/services/peripheral_service.py)
class PeripheralService:
@staticmethod
def get_peripheral_with_device(
peripheral_id: int,
db_peripherals: Session,
db_main: Session
):
"""Récupère périphérique avec device lié"""
peripheral = db_peripherals.query(Peripheral).get(peripheral_id)
result = {
"peripheral": peripheral,
"linked_device": None,
"benchmarks": []
}
if peripheral.linked_device_id:
device = db_main.query(Device).get(peripheral.linked_device_id)
result["linked_device"] = device
if device:
result["benchmarks"] = device.benchmarks
return result
@staticmethod
def compress_image(file_path: str, quality: int = 85):
"""Compresse une image"""
from PIL import Image
img = Image.open(file_path)
# ... compression logic
pass
@staticmethod
def generate_thumbnail(file_path: str, size: int = 300):
"""Génère thumbnail"""
pass
Main App (/backend/app/main.py)
from app.api import peripherals, locations, loans
if settings.PERIPHERALS_MODULE_ENABLED:
app.include_router(peripherals.router)
app.include_router(locations.router)
app.include_router(loans.router)
🎨 ARCHITECTURE FRONTEND
Pages HTML
/frontend/
├── peripherals.html # Page principale périphériques
├── peripheral_detail.html # Détail périphérique (optionnel)
├── locations.html # Gestion emplacements
├── loans.html # Gestion prêts
└── (pages existantes)
JavaScript
/frontend/js/
├── api.js # Extension BenchAPI.Peripherals.*
├── peripherals.js # Logique page principale
├── peripheral_detail.js # Logique détail
├── locations.js # Gestion emplacements
├── loans.js # Gestion prêts
├── components/
│ ├── PhotoGallery.js # Galerie photos
│ ├── DocumentList.js # Liste documents
│ ├── PeripheralFilters.js # Filtres avancés
│ ├── LocationSelector.js # Sélecteur arborescence
│ ├── RatingStars.js # Système étoiles
│ ├── TypeManager.js # Gestion types YAML
│ ├── StockManager.js # Gestion stock
│ └── ImageUploader.js # Upload images
└── utils.js # (existant, étendre si besoin)
Extension API Client (/frontend/js/api.js)
window.BenchAPI = window.BenchAPI || {};
window.BenchAPI.Peripherals = {
// CRUD
getPeripherals: async (page = 1, filters = {}) => {
const params = new URLSearchParams({ page, ...filters });
return await fetch(`${API_URL}/peripherals?${params}`).then(r => r.json());
},
getPeripheral: async (id) => {
return await fetch(`${API_URL}/peripherals/${id}`).then(r => r.json());
},
createPeripheral: async (data) => {
return await fetch(`${API_URL}/peripherals`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
}).then(r => r.json());
},
updatePeripheral: async (id, data) => {
return await fetch(`${API_URL}/peripherals/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
}).then(r => r.json());
},
deletePeripheral: async (id) => {
return await fetch(`${API_URL}/peripherals/${id}`, {
method: 'DELETE'
});
},
// Photos
uploadPhoto: async (peripheralId, file, compress = true) => {
const formData = new FormData();
formData.append('file', file);
formData.append('compress', compress);
return await fetch(`${API_URL}/peripherals/${peripheralId}/photos`, {
method: 'POST',
body: formData
}).then(r => r.json());
},
getPhotos: async (peripheralId) => {
return await fetch(`${API_URL}/peripherals/${peripheralId}/photos`)
.then(r => r.json());
},
// Documents
uploadDocument: async (peripheralId, file, docType) => {
const formData = new FormData();
formData.append('file', file);
formData.append('doc_type', docType);
return await fetch(`${API_URL}/peripherals/${peripheralId}/documents`, {
method: 'POST',
body: formData
}).then(r => r.json());
},
// Liens
addLink: async (peripheralId, link) => {
return await fetch(`${API_URL}/peripherals/${peripheralId}/links`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(link)
}).then(r => r.json());
},
// Prêts
createLoan: async (peripheralId, loanData) => {
return await fetch(`${API_URL}/peripherals/${peripheralId}/loan`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(loanData)
}).then(r => r.json());
},
returnLoan: async (loanId, returnData) => {
return await fetch(`${API_URL}/peripherals/loans/${loanId}/return`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(returnData)
}).then(r => r.json());
},
getActiveLoans: async () => {
return await fetch(`${API_URL}/peripherals/loans/active`)
.then(r => r.json());
},
// Benchmarks (appareils complets)
linkDevice: async (peripheralId, deviceId) => {
return await fetch(`${API_URL}/peripherals/${peripheralId}/link-device`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ device_id: deviceId })
}).then(r => r.json());
},
getBenchmarks: async (peripheralId) => {
return await fetch(`${API_URL}/peripherals/${peripheralId}/benchmarks`)
.then(r => r.json());
},
// Recherche
search: async (query, filters = {}) => {
const params = new URLSearchParams({ q: query, ...filters });
return await fetch(`${API_URL}/peripherals/search?${params}`)
.then(r => r.json());
},
// Config
getTypes: async () => {
return await fetch(`${API_URL}/peripherals/types`)
.then(r => r.json());
}
};
// Locations
window.BenchAPI.Locations = {
getLocations: async () => {
return await fetch(`${API_URL}/locations`).then(r => r.json());
},
getLocationTree: async () => {
return await fetch(`${API_URL}/locations/tree`).then(r => r.json());
},
createLocation: async (data) => {
return await fetch(`${API_URL}/locations`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
}).then(r => r.json());
}
};
Composants Réutilisables
PhotoGallery.js
class PhotoGallery {
constructor(containerId, peripheralId) {
this.container = document.getElementById(containerId);
this.peripheralId = peripheralId;
this.photos = [];
}
async load() {
this.photos = await BenchAPI.Peripherals.getPhotos(this.peripheralId);
this.render();
}
render() {
// Grid layout avec lightbox
this.container.innerHTML = `
<div class="photo-grid">
${this.photos.map(p => `
<div class="photo-item" data-id="${p.id}">
<img src="${p.thumbnail_path || p.stored_path}"
alt="${p.description}"
onclick="PhotoGallery.openLightbox('${p.stored_path}')">
${p.is_primary ? '<span class="badge-primary">Principal</span>' : ''}
<button onclick="PhotoGallery.delete(${p.id})">×</button>
</div>
`).join('')}
<div class="photo-upload">
<input type="file" id="upload-photo" accept="image/*" multiple>
<label for="upload-photo">+ Ajouter photos</label>
</div>
</div>
`;
}
static openLightbox(imagePath) {
// Lightbox overlay
}
}
RatingStars.js
class RatingStars {
constructor(containerId, currentRating = 0, editable = true) {
this.container = document.getElementById(containerId);
this.rating = currentRating;
this.editable = editable;
this.render();
}
render() {
const stars = [];
for (let i = 1; i <= 5; i++) {
const filled = i <= Math.round(this.rating);
stars.push(`
<span class="star ${filled ? 'filled' : ''}"
data-rating="${i}"
${this.editable ? `onclick="this.setRating(${i})"` : ''}>
★
</span>
`);
}
this.container.innerHTML = `
<div class="rating-stars">
${stars.join('')}
<span class="rating-value">${this.rating.toFixed(1)}/5</span>
</div>
`;
}
setRating(value) {
this.rating = value;
this.render();
// Trigger event
this.container.dispatchEvent(new CustomEvent('ratingChange', {
detail: { rating: value }
}));
}
}
CSS Additions (/frontend/css/components.css)
/* Périphériques */
.peripheral-card {
background: var(--card-bg);
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
transition: transform 0.2s;
}
.peripheral-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.3);
}
.peripheral-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.peripheral-type-badge {
background: var(--color-info);
color: #000;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.875rem;
}
.peripheral-status {
display: flex;
gap: 0.5rem;
}
.status-badge {
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
}
.status-neuf { background: #a6e22e; color: #000; }
.status-bon { background: #66d9ef; color: #000; }
.status-use { background: #fd971f; color: #000; }
.status-defectueux { background: #f92672; color: #fff; }
/* Rating Stars */
.rating-stars {
display: flex;
align-items: center;
gap: 0.25rem;
}
.star {
font-size: 1.5rem;
color: #666;
cursor: pointer;
transition: color 0.2s;
}
.star.filled {
color: #ffd700;
}
.star:hover {
color: #ffd700;
}
/* Photo Gallery */
.photo-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 1rem;
}
.photo-item {
position: relative;
aspect-ratio: 1;
border-radius: 8px;
overflow: hidden;
cursor: pointer;
}
.photo-item img {
width: 100%;
height: 100%;
object-fit: cover;
}
.photo-upload {
border: 2px dashed var(--border-color);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: border-color 0.2s;
}
.photo-upload:hover {
border-color: var(--color-info);
}
/* Location Tree */
.location-tree {
list-style: none;
padding-left: 0;
}
.location-tree li {
padding-left: 1.5rem;
position: relative;
}
.location-tree li:before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 1px;
background: var(--border-color);
}
.location-item {
padding: 0.5rem;
cursor: pointer;
border-radius: 4px;
}
.location-item:hover {
background: rgba(255,255,255,0.1);
}
.location-item.selected {
background: var(--color-info);
color: #000;
}
⚙️ FONCTIONNALITÉS PRINCIPALES
1. GESTION STOCK
- Quantités: Totale, Disponible, Seuil d'alerte
- Alertes: Notification si stock bas
- Mouvements: Tracking ajouts/retraits
- Emplacement: Stockage physique
2. COMPRESSION D'IMAGES
Configuration (image_processing.yaml):
compression:
enabled: true
original:
keep: true
path: "originals/"
optimized:
format: "webp"
quality: 85
max_width: 1920
max_height: 1080
thumbnail:
enabled: true
format: "webp"
quality: 75
width: 300
height: 300
fit: "cover"
max_upload_size_mb: 10
3. RATING SYSTÈME (0-5 ÉTOILES)
- Interface clickable
- Affichage visuel (★★★★☆)
- Stockage float (précision 0.1)
- Filtrage par rating
4. RECHERCHE AVANCÉE
Critères de recherche:
- Texte libre (nom, marque, modèle)
- Type principal / Sous-type
- Marque
- État (Neuf, Bon, Usagé, Défectueux)
- Prix (range)
- Date d'achat (range)
- Tags
- Localisation
- En prêt / Disponible
- Rating minimum
- Vendor ID / Product ID (Linux)
Résultats:
- Temps réel (<200ms)
- Pagination
- Tri multi-colonnes
- Export CSV/JSON
5. IMPORT/EXPORT
Export:
- CSV (Excel compatible)
- JSON (backup complet)
- PDF (fiche périphérique)
- Labels (impression étiquettes)
Import:
- CSV en masse
- Auto-détection colonnes
- Validation données
- Preview avant import
6. QR CODES
- Génération automatique par périphérique
- Lien vers fiche web
- Impression étiquettes
- Scan mobile → accès direct
📍 SYSTÈME DE LOCALISATION
Hiérarchie
📍 Racine
├── 🏠 Pièce (piece)
│ ├── 🗄️ Placard (placard)
│ │ ├── 🗃️ Tiroir (tiroir)
│ │ └── 📦 Étagère (etagere)
│ └── 🖥️ Meuble (meuble)
└── 🏠 Pièce 2
Logique
Si périphérique assigné à un device:
location_auto = TRUE→ suit le device- Localisation = celle du device
Si périphérique stocké:
location_auto = FALSElocation_id→ emplacement de stockage précislocation_details→ précisions (ex: "2ème rangée, sachet antistatique")
Fonctionnalités
- Arborescence interactive (TreeView)
- Breadcrumb (Maison > Bureau > Placard A > Tiroir 2)
- Historique mouvements (table
peripheral_location_history) - QR code par emplacement (scan → voir contenu)
- Photos emplacements (aide visuelle)
- Rapport non-localisés (alerte périphériques sans emplacement)
Configuration YAML (locations.yaml)
locations:
- nom: "Maison"
type: "root"
children:
- nom: "Bureau"
type: "piece"
children:
- nom: "Bureau principal"
type: "meuble"
- nom: "Placard A"
type: "placard"
children:
- nom: "Tiroir 1"
type: "tiroir"
description: "Câbles et adaptateurs"
- nom: "Tiroir 2"
type: "tiroir"
description: "Périphériques USB"
🤝 SYSTÈME DE PRÊT
Workflow
-
Créer prêt
- Emprunteur (nom, email, tél)
- Dates (prêt, retour prévu)
- Caution (montant, type)
- État départ + photos
- Raison du prêt
-
Suivi automatique
- Rappel 2j avant échéance
- Rappel à l'échéance
- Rappel retard (tous les 2j)
- Statut:
en_cours,en_retard
-
Retour
- Date retour effectif
- État au retour + photos
- Problèmes constatés
- Caution rendue/retenue
Notifications (CRON)
Tâche quotidienne (check_and_send_reminders()):
- Vérifier prêts à échéance J-2
- Vérifier prêts à échéance J
- Vérifier prêts en retard
- Envoyer emails automatiques
Configuration (notifications.yaml):
loan_notifications:
enabled: true
rappels:
avant_echeance:
enabled: true
jours_avant: 2
retard:
enabled: true
frequence_jours: 2
max_rappels: 3
email:
enabled: true
smtp_server: "smtp.gmail.com"
from_email: "inventory@example.com"
templates:
rappel_avant:
sujet: "Rappel: Retour de {peripheral_name} prévu le {date_retour}"
corps: |
Bonjour {emprunteur_name},
Ce message pour vous rappeler que le périphérique suivant
doit être retourné le {date_retour} :
• {peripheral_name}
• Caution: {caution_montant}€
Merci.
Statistiques
- Total prêts en cours
- Valeur totale prêtée
- Prêts en retard
- Cautions en attente
- Top emprunteurs
- Score fiabilité emprunteur
- Blacklist (optionnel)
💻 APPAREILS COMPLETS
Concept
Les appareils complets (Desktop, Laptop, Tablet, Smartphone, Serveur, Console) sont des périphériques spéciaux qui:
- Peuvent avoir des benchmarks (
linked_device_id→devices.id) - Peuvent avoir des périphériques attachés (souris, clavier, écran)
- Ont des specs complètes (CPU, RAM, GPU, etc.)
- Peuvent être prêtés comme tout périphérique
Lien avec Benchmarks
Peripheral (Laptop Dell XPS 15)
├─ is_complete_device = TRUE
├─ device_type = "laptop"
├─ linked_device_id = 42 → devices.id (dans data.db)
│
└─ → Accès aux benchmarks du device 42
├─ Historique benchmarks
├─ Dernier score global
├─ Évolution performances
└─ Hardware snapshots
Interface
Onglet "Benchmarks" dans fiche périphérique:
- Dernier benchmark (score global, détails)
- Historique (tableau, graphique évolution)
- Lien vers page device detail
- Bouton "Nouveau benchmark"
Sync automatique specs:
- Après chaque benchmark
- Mise à jour auto des specs (CPU, RAM, GPU, etc.)
- Depuis
hardware_snapshot
Périphériques Attachés
Exemple Desktop Gaming:
Desktop Gaming PC
├─ Linked device → Benchmarks
├─ Périphériques attachés:
│ ├─ Souris Logitech G Pro
│ ├─ Clavier Corsair K95
│ ├─ Casque SteelSeries
│ └─ Écran ASUS ROG 27"
└─ Localisation: Bureau > Bureau gaming
Gestion:
- Bouton "Attacher périphérique"
- Liste avec possibilité détacher
- Tracking historique
📝 CONFIGURATION YAML
Fichiers de Configuration
/backend/config/
├── peripheral_types.yaml # Types & catégories
├── locations.yaml # Emplacements
├── image_processing.yaml # Compression images
└── notifications.yaml # Alertes & emails
peripheral_types.yaml
Structure extensible pour définir les types de périphériques:
categories:
- id: connectivity
nom: "Connectivité"
icon: "plug"
types:
- id: usb_storage
nom: "Stockage USB"
icon: "usb-drive"
champs_specifiques:
- capacite_go:
type: "number"
required: true
label: "Capacité (Go)"
- usb_version:
type: "select"
options: ["2.0", "3.0", "3.1", "3.2"]
label: "Version USB"
- vitesse_lecture_mbs:
type: "number"
label: "Vitesse lecture (MB/s)"
- id: cables
nom: "Câbles"
icon: "cable"
types:
- id: cable_hdmi
nom: "Câble HDMI"
champs_specifiques:
- longueur_m:
type: "number"
required: true
label: "Longueur (m)"
- version_hdmi:
type: "select"
options: ["1.4", "2.0", "2.1"]
- certification:
type: "select"
options: ["Standard", "Premium", "Ultra"]
- id: complete_devices
nom: "Appareils Complets"
icon: "devices"
is_special: true
types:
- id: desktop
nom: "PC de Bureau (Desktop)"
is_complete_device: true
can_have_benchmarks: true
champs_specifiques:
- systeme_exploitation:
type: "select"
options: ["Windows 11", "Windows 10", "Linux", "macOS"]
- cpu_modele:
type: "text"
placeholder: "ex: Core i7-12700K"
# ... (voir section Types de Périphériques pour specs complètes)
- id: console_gaming
nom: "Console de Jeu"
icon: "gamepad"
is_complete_device: true
champs_specifiques:
- type_console:
type: "select"
options: ["Salon", "Portable", "Hybrid"]
- marque:
type: "select"
options: ["PlayStation", "Xbox", "Nintendo", "Steam", "Autre"]
# ... (voir section Types de Périphériques pour specs complètes)
# Types personnalisés ajoutés dynamiquement depuis l'interface
custom_types: []
Gestion Dynamique des Types
Interface admin (TypeManager.js):
- Modal gestion types
- Ajout/édition/suppression catégories
- Ajout/édition/suppression types
- Configuration champs spécifiques
- Sauvegarde YAML automatique
Avantages:
- ✅ Pas besoin modifier code pour nouveau type
- ✅ Interface [+] dans formulaires
- ✅ Persistance YAML
- ✅ Sync base de données automatique
📐 PLAN DE DÉPLOIEMENT
PHASE 1: Backend - Base de Données et API (Semaine 1-2)
1.1 Configuration et Sessions
- Mettre à jour
/backend/app/core/config.py- Ajouter
PERIPHERALS_DB_URL - Ajouter paramètres compression images
- Ajouter
PERIPHERALS_MODULE_ENABLED
- Ajouter
- Créer
/backend/app/db/session.py(deux sessions)get_db()→ DB principaleget_peripherals_db()→ DB périphériques
- Créer
BasePeripheralsdans/backend/app/db/base.py
1.2 Modèles SQLAlchemy
- Créer
/backend/app/models/peripheral.pyPeripheral(table principale)PeripheralPhotoPeripheralDocumentPeripheralLinkPeripheralLoan
- Créer
/backend/app/models/location.pyLocation(hiérarchie emplacements)
- Créer
/backend/app/models/peripheral_history.pyPeripheralLocationHistory
1.3 Schémas Pydantic
- Créer
/backend/app/schemas/peripheral.pyPeripheralBase,PeripheralCreate,PeripheralUpdatePeripheralDetail,PeripheralSummary,PeripheralListResponse
- Créer
/backend/app/schemas/location.py - Créer
/backend/app/schemas/loan.py
1.4 Services
- Créer
/backend/app/services/peripheral_service.pyget_peripheral_with_device()(cross-DB)link_peripheral_to_device()compress_image(),generate_thumbnail()sync_peripheral_from_benchmark()
- Créer
/backend/app/services/location_service.pyget_location_tree()(hiérarchie)get_full_location_path()
- Créer
/backend/app/services/loan_service.pycheck_and_send_reminders()(CRON)send_loan_reminder()
1.5 Routes API
- Créer
/backend/app/api/peripherals.py(20+ endpoints)- CRUD périphériques
- Photos, Documents, Liens
- Prêts, Localisation, Historique
- Recherche avancée
- Appareils complets (link device, benchmarks)
- Créer
/backend/app/api/locations.py- CRUD emplacements
- Arborescence
- QR codes
- Créer
/backend/app/api/loans.py- CRUD prêts
- Retours
- Rappels
- Statistiques
1.6 Configuration YAML
- Créer
/backend/config/peripheral_types.yaml- Définir toutes les catégories
- Définir tous les types (USB, Câbles, Desktop, Console, etc.)
- Champs spécifiques par type
- Créer
/backend/config/locations.yaml- Structure initiale emplacements
- Créer
/backend/config/image_processing.yaml- Paramètres compression
- Créer
/backend/config/notifications.yaml- Templates emails
- Configuration SMTP
1.7 Utilitaires
- Créer
/backend/app/utils/yaml_loader.py- Charger/sauvegarder YAML
- Parser configurations
- Créer
/backend/app/utils/image_processor.py- Compression images (Pillow)
- Génération thumbnails
- Validation formats
- Créer
/backend/app/utils/qr_generator.py- Génération QR codes
- Créer
/backend/app/tasks/daily_loan_reminders.py- Tâche CRON quotidienne
1.8 Initialisation
- Mettre à jour
/backend/app/db/init_db.py- Créer tables
peripherals.db - Charger YAML initial
- Créer emplacements par défaut
- Créer tables
- Mettre à jour
/backend/app/main.py- Include routers peripherals
- Check
PERIPHERALS_MODULE_ENABLED
PHASE 2: Frontend - Interface Utilisateur (Semaine 3-4)
2.1 Pages HTML
- Créer
/frontend/peripherals.html- Layout deux panneaux
- Liste + Détails/Formulaire
- Navigation onglets
- Créer
/frontend/peripheral_detail.html(optionnel)- Vue complète périphérique
- Onglets: Infos, Photos, Docs, Liens, Historique, Benchmarks
- Créer
/frontend/locations.html- Gestion emplacements
- Arborescence interactive
- Créer
/frontend/loans.html- Liste prêts en cours
- Historique
- Statistiques
2.2 JavaScript - API Client
- Étendre
/frontend/js/api.jsBenchAPI.Peripherals.*(CRUD, photos, docs, liens, prêts)BenchAPI.Locations.*BenchAPI.Loans.*
2.3 JavaScript - Logique Pages
- Créer
/frontend/js/peripherals.js- Initialisation page
- Chargement liste
- Recherche/filtres temps réel
- Formulaire dynamique (selon type)
- Upload médias
- Créer
/frontend/js/peripheral_detail.js- Gestion onglets
- Affichage benchmarks
- Galerie photos
- Créer
/frontend/js/locations.js- TreeView emplacements
- CRUD emplacements
- QR codes
- Créer
/frontend/js/loans.js- Formulaire prêt
- Formulaire retour
- Liste prêts
- Rappels
2.4 Composants Réutilisables
- Créer
/frontend/js/components/PhotoGallery.js- Grid photos
- Upload drag & drop
- Lightbox
- Compression option
- Créer
/frontend/js/components/DocumentList.js- Liste documents avec icônes
- Upload
- Preview PDF
- Créer
/frontend/js/components/PeripheralFilters.js- Filtres latéraux
- Recherche avancée
- Tags
- Créer
/frontend/js/components/LocationSelector.js- TreeView sélection
- Breadcrumb
- Créer
/frontend/js/components/RatingStars.js- Système étoiles clickable
- Affichage/édition
- Créer
/frontend/js/components/TypeManager.js- Modal gestion types YAML
- CRUD types/catégories
- Créer
/frontend/js/components/StockManager.js- Gestion quantités
- Alertes stock
- Créer
/frontend/js/components/ImageUploader.js- Upload multiple
- Preview
- Crop/rotate basique
2.5 CSS
- Étendre
/frontend/css/components.css- Styles périphériques
- Cards
- Badges (type, état, prêt)
- Photo gallery
- Rating stars
- Location tree
- Modals
- Forms dynamiques
2.6 Navigation
- Mettre à jour TOUTES les pages HTML
- Ajouter lien "Périphériques" dans nav
- Ordre: Dashboard | Devices | Périphériques | Prêts | Emplacements | Settings
PHASE 3: Fonctionnalités Avancées (Semaine 5)
3.1 Recherche Avancée
- Interface recherche multi-critères
- Résultats temps réel
- Filtres combinables
- Tri multi-colonnes
3.2 Import/Export
- Export CSV/JSON
- Import CSV en masse
- Export PDF fiche périphérique
- Impression étiquettes
3.3 QR Codes
- Génération QR par périphérique
- Génération QR par emplacement
- Page scan → fiche
- Impression batch
3.4 Statistiques
- Dashboard périphériques
- Total par type (Pie chart)
- Valeur inventaire
- Top marques (Bar chart)
- État (Donut chart)
- Dashboard prêts
- Prêts en cours
- Valeur prêtée
- Alertes retards
- Dashboard emplacements
- Inventaire par emplacement
- Valeur par pièce
PHASE 4: Intégration et Optimisation (Semaine 6)
4.1 Intégration Devices
- Dans
device_detail.html, ajouter onglet "Périphériques connectés" - Bouton "Créer fiche périphérique" depuis device
- Auto-link lors création
- Sync specs après benchmark
4.2 Détection Automatique
- Parser
lsusboutput - Parser
lspcioutput - Suggérer ajout auto nouveaux périphériques
- Pré-remplir vendor/product ID
4.3 Notifications
- Setup CRON pour rappels prêts
- Templates emails
- Configuration SMTP
- Alertes stock bas
- Alertes garantie expirée
4.4 Performance
- Optimisation queries SQL (eager loading)
- Index appropriés
- Compression images
- Cache recherches fréquentes
- Pagination efficace
4.5 Sécurité
- Validation uploads (MIME, taille)
- Sanitization noms fichiers
- Protection CSRF
- Rate limiting API
PHASE 5: Documentation et Tests (Semaine 7)
5.1 Documentation
/docs/PERIPHERALS_USER_GUIDE.md- Guide utilisateur
- Exemples d'utilisation
- FAQ
/docs/PERIPHERALS_API.md- Documentation API (Swagger)
- Exemples requêtes
/docs/PERIPHERALS_ADMIN.md- Configuration YAML
- Gestion types
- Maintenance
5.2 Exemples et Fixtures
- Script seed données test
- Templates JSON par type
- Données d'exemple
5.3 Tests
- Tests unitaires backend (pytest)
- Models
- Services
- Utils
- Tests intégration API
- Endpoints CRUD
- Cross-DB queries
- Tests frontend
- Validation formulaires
- Composants
🚀 PROMPT DE DÉVELOPPEMENT
Contexte du Projet
Vous êtes chargé d'implémenter le Module Périphériques pour l'application Linux BenchTools existante. Cette application permet actuellement de benchmarker des machines Linux et de stocker leurs résultats.
Architecture Existante
Backend:
- FastAPI + SQLAlchemy
- Base de données: SQLite
data.db - Structure:
/backend/app/avec models/, schemas/, api/, core/ - Authentification par token
Frontend:
- Vanilla JavaScript (pas de framework)
- Thème: Monokai dark avec CSS variables
- Layout: Two-panel design
- Navigation: Dashboard, Devices, Settings
Objectif du Module
Créer un système complet de gestion d'inventaire de périphériques avec:
- Catalogue exhaustif de périphériques (USB, Bluetooth, Câbles, Visserie, Appareils complets, Consoles)
- Localisation physique (Pièces → Placards → Tiroirs)
- Gestion de prêts (avec rappels automatiques)
- Intégration benchmarks (pour Desktop, Laptop, Serveur)
- Fonctionnalités avancées (Stock, Rating, Photos, Documents, QR codes)
Décisions Architecturales Clés
1. Base de Données Séparée
IMPORTANT: Créer une nouvelle base de données peripherals.db séparée de data.db.
Pourquoi:
- Isolation des données
- Performance (pas de contention)
- Backups indépendants
- Module activable/désactivable
Lien entre DB:
peripherals.linked_device_id→ référence logique versdevices.id(pas FK SQL)- Gérer relations manuellement côté application via service layer
2. Configuration YAML Dynamique
Les types de périphériques sont définis en YAML, modifiables depuis l'interface:
# /backend/config/peripheral_types.yaml
categories:
- id: cables
nom: "Câbles"
types:
- id: cable_hdmi
nom: "Câble HDMI"
champs_specifiques:
- longueur_m: { type: "number", required: true }
- version_hdmi: { type: "select", options: ["1.4", "2.0", "2.1"] }
Avantages:
- Pas de modification code pour nouveau type
- Interface admin [+] pour ajouter types
- Persistance automatique
3. Appareils Complets avec Benchmarks
Les Desktop, Laptop, Serveur, Console sont des périphériques spéciaux:
is_complete_device = TRUElinked_device_id→ lien versdevices.idpour benchmarks- Affichage historique benchmarks intégré
- Sync automatique specs depuis
hardware_snapshot
4. Système de Localisation Hiérarchique
Arborescence emplacements:
Maison
├─ Bureau (piece)
│ ├─ Placard A (placard)
│ │ ├─ Tiroir 1 (tiroir)
│ │ └─ Tiroir 2 (tiroir)
│ └─ Bureau principal (meuble)
└─ Salon (piece)
Logique:
- Si
device_id != NULLetlocation_auto = TRUE→ localisation = celle du device - Sinon →
location_id(emplacement de stockage)
5. Compression d'Images Automatique
Configuration dans image_processing.yaml:
- Format optimisé: WebP
- Qualité: 85%
- Thumbnails: 300x300px
- Conserver original optionnel
Structure des Données
Table Principale: peripherals
Champs globaux (tous périphériques):
- Identification (nom, type, marque, modèle, S/N)
- Achat (boutique, date, prix, garantie)
- Évaluation (rating 0-5 étoiles)
- Stock (quantité totale, disponible, seuil alerte)
- Métadonnées (dates, état, tags, notes)
- Linux (device_path, vendor_id, product_id, driver)
- Installation (auto, driver requis, problèmes connus)
- Connectivité (interface, consommation)
- Localisation (location_id, location_auto)
- Prêt (en_pret, pret_actuel_id, prete_a)
- Appareil complet (is_complete_device, linked_device_id)
- Données spécifiques (JSON flexible par type)
Relations:
peripheral_photos(1:N, photos avec principale)peripheral_documents(1:N, manuels/factures/datasheets)peripheral_links(1:N, liens fabricant/support/drivers)peripheral_loans(1:N, historique prêts)peripheral_location_history(1:N, mouvements)locations(hiérarchie N niveaux)
Implémentation Backend
Sessions DB
# /backend/app/db/session.py
engine_main = create_engine(settings.DATABASE_URL) # data.db
engine_peripherals = create_engine(settings.PERIPHERALS_DB_URL) # peripherals.db
def get_db() -> Session:
"""Session DB principale (benchmarks)"""
...
def get_peripherals_db() -> Session:
"""Session DB périphériques"""
...
Modèles
# /backend/app/models/peripheral.py
from app.db.base import BasePeripherals # ← Pas Base !
class Peripheral(BasePeripherals):
__tablename__ = "peripherals"
# ... champs
# PAS de relationship SQLAlchemy vers data.db
# Gérer manuellement via service layer
Service Layer (Cross-DB)
# /backend/app/services/peripheral_service.py
class PeripheralService:
@staticmethod
def get_peripheral_with_device(
peripheral_id: int,
db_peripherals: Session,
db_main: Session
):
"""Récupère périphérique + device lié (cross-DB)"""
peripheral = db_peripherals.query(Peripheral).get(peripheral_id)
if peripheral.linked_device_id:
device = db_main.query(Device).get(peripheral.linked_device_id)
benchmarks = device.benchmarks if device else []
return {
"peripheral": peripheral,
"linked_device": device,
"benchmarks": benchmarks
}
return {"peripheral": peripheral}
Routes API (Deux Sessions)
# /backend/app/api/peripherals.py
@router.get("/{peripheral_id}")
async def get_peripheral(
peripheral_id: int,
db_peripherals: Session = Depends(get_peripherals_db), # ← DB périphériques
db_main: Session = Depends(get_db) # ← DB principale
):
"""Détail avec benchmarks si appareil complet"""
return PeripheralService.get_peripheral_with_device(
peripheral_id, db_peripherals, db_main
)
Implémentation Frontend
Structure
/frontend/
├── peripherals.html # Page principale (two-panel)
├── peripheral_detail.html # Détail (optionnel)
├── locations.html # Gestion emplacements
├── loans.html # Gestion prêts
├── js/
│ ├── api.js # Extension BenchAPI.Peripherals.*
│ ├── peripherals.js # Logique page
│ ├── components/
│ │ ├── PhotoGallery.js # Galerie + upload
│ │ ├── RatingStars.js # Système étoiles
│ │ ├── LocationSelector.js # TreeView
│ │ └── TypeManager.js # Gestion types YAML
│ └── ...
└── css/
└── components.css # Styles périphériques
API Client Extension
// /frontend/js/api.js
window.BenchAPI.Peripherals = {
getPeripherals: async (page, filters) => { /* ... */ },
getPeripheral: async (id) => { /* ... */ },
createPeripheral: async (data) => { /* ... */ },
uploadPhoto: async (peripheralId, file, compress = true) => { /* ... */ },
createLoan: async (peripheralId, loanData) => { /* ... */ },
linkDevice: async (peripheralId, deviceId) => { /* ... */ },
getBenchmarks: async (peripheralId) => { /* ... */ },
// ... 20+ méthodes
};
Composants Réutilisables
PhotoGallery:
class PhotoGallery {
constructor(containerId, peripheralId) { /* ... */ }
async load() { /* Charger photos */ }
render() { /* Grid + lightbox */ }
async upload(files) { /* Upload avec compression */ }
}
RatingStars:
class RatingStars {
constructor(containerId, currentRating = 0, editable = true) { /* ... */ }
render() { /* ★★★★☆ */ }
setRating(value) { /* Clickable */ }
}
LocationSelector:
class LocationSelector {
constructor(containerId) { /* ... */ }
async loadTree() { /* Arborescence depuis API */ }
render() { /* TreeView avec expand/collapse */ }
getSelectedPath() { /* Breadcrumb */ }
}
Fonctionnalités Clés à Implémenter
1. Gestion de Prêt avec Notifications
Backend:
- CRON quotidien (
check_and_send_reminders()) - Rappels 2j avant, à échéance, en retard
- Templates emails configurables (YAML)
Frontend:
- Modal prêt (emprunteur, dates, caution, photos)
- Modal retour (état, problèmes, caution rendue)
- Liste prêts actifs
- Alertes retards
2. Compression d'Images
# /backend/app/utils/image_processor.py
def compress_image(file_path: str, quality: int = 85):
from PIL import Image
img = Image.open(file_path)
# Resize si trop grand
if img.width > settings.IMAGE_MAX_WIDTH:
ratio = settings.IMAGE_MAX_WIDTH / img.width
new_size = (int(img.width * ratio), int(img.height * ratio))
img = img.resize(new_size, Image.LANCZOS)
# Convertir en WebP
output_path = file_path.replace(ext, '.webp')
img.save(output_path, 'WEBP', quality=quality)
return output_path
def generate_thumbnail(file_path: str, size: int = 300):
# Crop/resize 300x300
...
3. QR Codes
# /backend/app/utils/qr_generator.py
import qrcode
def generate_peripheral_qr(peripheral_id: int):
url = f"{settings.FRONTEND_URL}/peripherals/{peripheral_id}"
img = qrcode.make(url)
path = f"/uploads/peripherals/qr_{peripheral_id}.png"
img.save(path)
return path
4. Détection Auto Linux
# /backend/app/services/detection_service.py
def parse_lsusb_output(output: str) -> List[Dict]:
"""Parse lsusb et suggère périphériques"""
devices = []
for line in output.split('\n'):
# Parse: Bus 001 Device 003: ID 046d:c52b Logitech, Inc. Unifying Receiver
if match := re.match(r'Bus (\d+) Device (\d+): ID ([0-9a-f]{4}):([0-9a-f]{4}) (.+)', line):
devices.append({
'vendor_id': match.group(3),
'product_id': match.group(4),
'description': match.group(5),
'suggested_name': parse_device_name(match.group(5))
})
return devices
Considérations Importantes
Thème et UX
- Cohérence visuelle: Utiliser le même thème Monokai que l'existant
- Two-panel layout: Liste gauche (20%) + Détails/Formulaire droite (80%)
- Icons: Icônes SVG par type de périphérique
- Badges: Colorés selon état (Neuf=vert, Usagé=orange, Défectueux=rouge)
- Responsive: Grid layout adaptatif
Performance
- Pagination: 50 items par page
- Lazy loading: Images chargées à la demande
- Cache: Recherches fréquentes (types, marques)
- Index DB: Sur tous les champs de recherche
Sécurité
- Validation uploads: MIME type, taille max (10MB)
- Sanitization: Noms fichiers (supprimer caractères spéciaux)
- CORS: Restreindre origines
- Rate limiting: 100 req/min par IP
Extensibilité
- YAML dynamique: Nouveaux types sans modifier code
- JSON flexible:
caracteristiques_specifiquesadapté à chaque type - Plugins: Architecture permettant ajout de modules (future)
Checklist d'Implémentation
Backend ✓
- Deux bases de données (sessions séparées)
- Modèles SQLAlchemy (BasePeripherals)
- Schémas Pydantic (CRUD + relations)
- Service layer (cross-DB queries)
- Routes API (20+ endpoints)
- Configuration YAML (types, locations, images, notifications)
- Compression images (Pillow)
- QR codes (qrcode lib)
- CRON notifications (rappels prêts)
- Init DB (création tables + seed data)
Frontend ✓
- Pages HTML (peripherals, locations, loans)
- Extension API client (BenchAPI.Peripherals.*)
- Logique pages (peripherals.js, locations.js, loans.js)
- Composants réutilisables (PhotoGallery, RatingStars, LocationSelector, TypeManager)
- CSS cohérent (thème Monokai)
- Navigation mise à jour (toutes les pages)
Fonctionnalités ✓
- CRUD périphériques
- Upload photos/documents
- Gestion prêts (création, retour, rappels)
- Localisation (arborescence, historique)
- Appareils complets (lien benchmarks)
- Recherche avancée
- Stock management
- Rating 0-5 étoiles
- QR codes
- Import/Export CSV/JSON
Tests & Documentation ✓
- Tests unitaires backend
- Tests intégration API
- Documentation utilisateur
- Documentation API (Swagger)
- Exemples et fixtures
Commandes Utiles
# Créer les bases de données
cd /backend
python -c "from app.db.init_db import init_db; init_db()"
# Lancer backend
cd /backend
uvicorn app.main:app --reload --host 0.0.0.0 --port 8007
# Tests
pytest tests/
# Restart Docker
docker-compose restart backend
Points d'Attention
- Ne JAMAIS créer de FK SQL entre data.db et peripherals.db (gérer manuellement)
- Toujours utiliser deux sessions dans les routes avec cross-DB queries
- Valider YAML lors du chargement (schéma pydantic)
- Compresser images par défaut (économie espace)
- Logger toutes les actions (historique, audit)
- Tester avec charge (1000+ périphériques)
Résultat Attendu
Un module périphériques complet, professionnel et extensible qui:
✅ Permet d'inventorier TOUT le matériel informatique ✅ Localise précisément chaque élément ✅ Gère les prêts avec rappels automatiques ✅ S'intègre aux benchmarks existants ✅ Offre une recherche rapide et puissante ✅ Est facile à étendre (types YAML) ✅ Respecte le design existant (Monokai, two-panel) ✅ Fonctionne en production (sécurisé, performant)
Bonne implémentation ! 🚀