""" Utilitaires de formatage pour les templates Jinja2 GMAO Collège """ from datetime import timezone from zoneinfo import ZoneInfo from flask import current_app def local_datetime(value): """Convertit un instant stocké en UTC vers le fuseau configuré.""" if value is None: return None if value.tzinfo is None: value = value.replace(tzinfo=timezone.utc) zone_name = current_app.config.get('TIMEZONE', 'Europe/Paris') return value.astimezone(ZoneInfo(zone_name)) def format_datetime(value, format_string='%d/%m/%Y %H:%M'): """Formate un instant UTC dans l'heure locale de l'établissement.""" converted = local_datetime(value) return converted.strftime(format_string) if converted else '' def format_status(status): """Formate le statut pour l'affichage.""" status_map = { 'en_attente': 'En attente', 'en_cours': 'En cours', 'en_attente_pieces': 'En attente pièces', 'en_attente_entreprise': 'En attente entreprise', 'en_attente_facture': 'En attente facture', 'terminee': 'Terminée', 'cloturee': 'Clôturée', 'annulee': 'Annulée', 'reportee': 'Reportée', 'urgence': 'Urgence', 'en_service': 'En service', 'hors_service': 'Hors service', 'en_reparation': 'En réparation', 'remplace': 'Remplacé', } return status_map.get(status, status) def status_badge_class(status): """Retourne la classe Bootstrap pour le badge de statut.""" badge_map = { 'en_attente': 'secondary', 'en_cours': 'primary', 'en_attente_pieces': 'warning', 'en_attente_entreprise': 'warning', 'en_attente_facture': 'warning', 'terminee': 'success', 'cloturee': 'dark', 'annulee': 'danger', 'reportee': 'info', 'urgence': 'danger', 'en_service': 'success', 'hors_service': 'danger', 'en_reparation': 'warning', 'remplace': 'secondary', } return badge_map.get(status, 'secondary') # Alias pour compatibilité templates status_badge = status_badge_class def priority_badge_class(priority): """Retourne la classe Bootstrap pour le badge de priorité.""" badge_map = { 'basse': 'secondary', 'normale': 'primary', 'haute': 'warning', 'urgente': 'danger', } return badge_map.get(priority, 'secondary') def bootstrap_color_to_hex(color): """ Convertit un nom de couleur Bootstrap en code hexadécimal. Si la couleur est déjà un hex (#rrggbb), la retourne telle quelle. Utilisé pour les types de salles et autres entités avec des couleurs. """ # Mapping des couleurs Bootstrap vers hex bootstrap_colors = { 'primary': '#0d6efd', 'secondary': '#6c757d', 'success': '#198754', 'danger': '#dc3545', 'warning': '#ffc107', 'info': '#0dcaf0', 'light': '#f8f9fa', 'dark': '#212529', 'purple': '#6f42c1', 'pink': '#d63384', 'orange': '#fd7e14', 'teal': '#20c997', 'cyan': '#0dcaf0', 'white': '#ffffff', 'black': '#000000', } if not color: return '#6c757d' # secondary par défaut # Si déjà un code hex, le retourner tel quel if color.startswith('#'): return color return bootstrap_colors.get(color.lower(), '#6c757d') def bootstrap_text_color(bg_color): """ Retourne la couleur de texte appropriée pour un fond donné. Calcule la luminosité relative (YIQ) pour décider du contraste. Utilisé avec hex_color dans les templates. """ if not bg_color: return 'white' # Couleurs Bootstrap claires par nom light_colors = {'warning', 'light', 'white', 'info', 'cyan', 'yellow', 'orange'} if bg_color.lower() in light_colors: return '#212529' # dark # Pour les codes hex, calculer la luminosité relative hex_color = bg_color # Convertir en hex si c'est un nom Bootstrap bootstrap_colors = { 'primary': '#0d6efd', 'secondary': '#6c757d', 'success': '#198754', 'danger': '#dc3545', 'warning': '#ffc107', 'info': '#0dcaf0', 'light': '#f8f9fa', 'dark': '#212529', 'purple': '#6f42c1', 'pink': '#d63384', 'orange': '#fd7e14', 'teal': '#20c997', 'cyan': '#0dcaf0', 'white': '#ffffff', 'black': '#000000', } if not bg_color.startswith('#'): hex_color = bootstrap_colors.get(bg_color.lower(), '#6c757d') # Extraire les composantes RGB try: hex_color = hex_color.lstrip('#') r = int(hex_color[0:2], 16) g = int(hex_color[2:4], 16) b = int(hex_color[4:6], 16) # Calculer la luminosité relative (formule YIQ) # Valeur entre 0 (noir) et 255 (blanc) # Seuil ~120 pour un bon contraste (perceptuellement) luminance = (0.299 * r + 0.587 * g + 0.114 * b) if luminance > 120: return '#212529' # Fond clair -> texte foncé else: return 'white' # Fond foncé -> texte blanc except (ValueError, IndexError): return 'white' # Par défaut