460 lines
No EOL
21 KiB
HTML
460 lines
No EOL
21 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Contexte GMAO - Configuration{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<div class="row mb-4">
|
|
<div class="col">
|
|
<h1>🧠 Contexte GMAO pour Hermes</h1>
|
|
<p class="text-muted">Configurez le contexte qui sera utilisé par Hermes pour interpréter chaque email</p>
|
|
</div>
|
|
<div class="col-auto">
|
|
<a href="{{ url_for('outlook_dashboard.auto_interpret_page') }}" class="btn btn-primary">
|
|
<i class="fas fa-robot"></i> Interprétation automatique
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Aperçu du contexte -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">📋 Aperçu du contexte actuel</h5>
|
|
<button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#contextPreview">
|
|
Afficher/Masquer
|
|
</button>
|
|
</div>
|
|
<div class="collapse show" id="contextPreview">
|
|
<div class="card-body">
|
|
<pre id="fullContextPreview" class="bg-light p-3 rounded" style="white-space: pre-wrap; max-height: 200px; overflow-y: auto;">Chargement...</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Prompt injecté -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card border-info">
|
|
<div class="card-header bg-info text-white d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">🤖 Prompt injecté à Hermes</h5>
|
|
<button class="btn btn-sm btn-outline-light" type="button" data-bs-toggle="collapse" data-bs-target="#promptPreview">
|
|
Afficher/Masquer
|
|
</button>
|
|
</div>
|
|
<div class="collapse" id="promptPreview">
|
|
<div class="card-body">
|
|
<p class="text-muted small mb-2">Ce prompt est envoyé à Hermes avec chaque email analysé. La première partie est fixe, le contexte GMAO est configurable ci-dessous.</p>
|
|
<p class="text-info small mb-2"><i class="fas fa-paperclip"></i> Les pièces jointes PDF sont automatiquement extraites et ajoutées au contenu avec <code>[CONTENU PDF]:</code></p>
|
|
<pre id="promptFullPreview" class="bg-dark text-light p-3 rounded" style="white-space: pre-wrap; max-height: 400px; overflow-y: auto; font-size: 12px;">Chargement...</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Formulaire de configuration -->
|
|
<form id="contextForm">
|
|
<div class="row">
|
|
<!-- Colonne gauche: Informations établissement -->
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card h-100">
|
|
<div class="card-header bg-primary text-white">
|
|
<h5 class="mb-0"><i class="fas fa-building"></i> Établissement</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label for="establishment_name" class="form-label">Nom de l'établissement</label>
|
|
<input type="text" class="form-control" id="establishment_name" name="establishment_name"
|
|
placeholder="Ex: Collège Jean Jaurès">
|
|
<div class="form-text">Le nom complet de votre collège</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="establishment_type" class="form-label">Type d'établissement</label>
|
|
<select class="form-select" id="establishment_type" name="establishment_type">
|
|
<option value="">-- Sélectionner --</option>
|
|
<option value="Collège public">Collège public</option>
|
|
<option value="Collège privé">Collège privé</option>
|
|
<option value="Lycée public">Lycée public</option>
|
|
<option value="Lycée privé">Lycée privé</option>
|
|
<option value="École primaire">École primaire</option>
|
|
<option value="Autre">Autre</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="address" class="form-label">Adresse complète</label>
|
|
<textarea class="form-control" id="address" name="address" rows="2"
|
|
placeholder="Ex: 15 rue de la République, 77000 Melun"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Colonne droite: Contacts -->
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card h-100">
|
|
<div class="card-header bg-info text-white">
|
|
<h5 class="mb-0"><i class="fas fa-address-book"></i> Contacts utiles</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label for="contact_info" class="form-label">Contacts et entreprises</label>
|
|
<textarea class="form-control" id="contact_info" name="contact_info" rows="6"
|
|
placeholder="Listez les contacts importants:
|
|
- Entreprise de chauffage: ABC Chauffage, Tél: 01 60 00 00 00
|
|
- Électricien: Jean Dupont, Tél: 06 00 00 00 00
|
|
- Mairie: Service technique, Tél: 01 60 00 00 01"></textarea>
|
|
<div class="form-text">Contacts des entreprises et prestataires</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Contexte technique -->
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card h-100">
|
|
<div class="card-header bg-secondary text-white">
|
|
<h5 class="mb-0"><i class="fas fa-cogs"></i> Contexte technique</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label for="technical_context" class="form-label">Informations techniques</label>
|
|
<textarea class="form-control" id="technical_context" name="technical_context" rows="5"
|
|
placeholder="Décrivez les bâtiments, surfaces, installations:
|
|
|
|
Ex: Le collège compte 3 bâtiments:
|
|
- Bâtiment A (principal): 2500m², 2 étages
|
|
- Bâtiment B (technologie): 800m²
|
|
- Bâtiment C (gymnase): 1200m²
|
|
|
|
Chauffage: gaz, chaudière Viessmann de 2015
|
|
VMC: centrale avec récupération de chaleur"></textarea>
|
|
<div class="form-text">Description des bâtiments, installations, surfaces</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Équipements -->
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card h-100">
|
|
<div class="card-header bg-warning">
|
|
<h5 class="mb-0"><i class="fas fa-tools"></i> Équipements principaux</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label for="equipment_context" class="form-label">Liste des équipements</label>
|
|
<textarea class="form-control" id="equipment_context" name="equipment_context" rows="5"
|
|
placeholder="Listez les équipements principaux:
|
|
|
|
Ex:
|
|
- Chaudière Viessmann Vitodens 200 (2015) - Bâtiment A
|
|
- VMC Aldes 800 m³/h - Bâtiment A
|
|
- Ascenseur ThyssenKrupp (2010) - Bâtiment A
|
|
- Portail automatique Nice - Entrée principale
|
|
- Climatiseur réversible Daikin (2020) - Bâtiment administration"></textarea>
|
|
<div class="form-text">Équipements à surveiller et maintenir</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Procédures -->
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card h-100">
|
|
<div class="card-header bg-danger text-white">
|
|
<h5 class="mb-0"><i class="fas fa-clipboard-list"></i> Procédures</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label for="procedure_context" class="form-label">Procédures spécifiques</label>
|
|
<textarea class="form-control" id="procedure_context" name="procedure_context" rows="5"
|
|
placeholder="Décrivez les procédures spécifiques:
|
|
|
|
Ex:
|
|
- Urgence chauffage: contacter ABC Chauffage au 01 60 00 00 00 (24h/24)
|
|
- Fuite d'eau: couper l'arrivée générale (vanne dans le local technique A)
|
|
- Alarme incendie: appeler les pompiers puis la direction
|
|
- Intervention externe: créer une demande via GMAO avant intervention"></textarea>
|
|
<div class="form-text">Procédures d'urgence et protocoles</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Personnel -->
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card h-100">
|
|
<div class="card-header bg-success text-white">
|
|
<h5 class="mb-0"><i class="fas fa-users"></i> Personnel</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label for="staff_info" class="form-label">Informations sur le personnel</label>
|
|
<textarea class="form-control" id="staff_info" name="staff_info" rows="5"
|
|
placeholder="Décrivez le personnel:
|
|
|
|
Ex:
|
|
- Éric (chef): Responsable maintenance
|
|
- Alban (technicien): Interventions quotidiennes, PRONOTE, téléphonie
|
|
- Martine (secrétariat): Demandes d'intervention, commandes
|
|
|
|
L'équipe technique compte 2 agents polyvalents."></textarea>
|
|
<div class="form-text">Rôles et responsabilités de l'équipe</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Instructions Hermes -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header bg-dark text-white">
|
|
<h5 class="mb-0"><i class="fas fa-robot"></i> Instructions personnalisées pour Hermes</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label for="hermes_instructions" class="form-label">Instructions spécifiques</label>
|
|
<textarea class="form-control" id="hermes_instructions" name="hermes_instructions" rows="4"
|
|
placeholder="Instructions personnalisées pour l'IA Hermes:
|
|
|
|
Ex:
|
|
- Toujours formuler les réponses en français
|
|
- Classer les demandes par ordre de priorité
|
|
- Proposer des interventions dans la GMAO quand c'est pertinent
|
|
- Mentionner les contacts appropriés pour chaque type de problème
|
|
- Indiquer si une intervention urgente est nécessaire"></textarea>
|
|
<div class="form-text">Instructions spécifiques pour orienter les réponses de Hermes</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Expéditeurs exclus -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0"><i class="fas fa-ban"></i> Expéditeurs exclus</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Emails à exclure de l'interprétation</label>
|
|
<textarea id="excluded_senders" class="form-control" rows="4"
|
|
placeholder="newsletter@exemple.com
|
|
publicite@entreprise.fr
|
|
noreply@service.fr
|
|
Un email par ligne - ces expéditeurs seront ignorés lors de l'analyse"></textarea>
|
|
<div class="form-text">Ces expéditeurs seront ignorés lors de l'analyse automatique des emails (newsletters, publicités, notifications système, etc.)</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Boutons d'action -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between">
|
|
<button type="button" class="btn btn-outline-secondary" onclick="resetForm()">
|
|
<i class="fas fa-undo"></i> Réinitialiser
|
|
</button>
|
|
<button type="submit" class="btn btn-success btn-lg">
|
|
<i class="fas fa-save"></i> Sauvegarder le contexte
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Toast pour les notifications -->
|
|
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
|
<div id="saveToast" class="toast" role="alert">
|
|
<div class="toast-header">
|
|
<strong class="me-auto">Contexte GMAO</strong>
|
|
<button type="button" class="btn-close" data-bs-dismiss="toast"></button>
|
|
</div>
|
|
<div class="toast-body" id="toastMessage">
|
|
Sauvegardé avec succès
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Charger le contexte existant
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadContext();
|
|
});
|
|
|
|
function loadContext() {
|
|
fetch('/outlook/api/context')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// Remplir le formulaire
|
|
document.getElementById('establishment_name').value = data.establishment_name || '';
|
|
document.getElementById('establishment_type').value = data.establishment_type || '';
|
|
document.getElementById('address').value = data.address || '';
|
|
document.getElementById('technical_context').value = data.technical_context || '';
|
|
document.getElementById('equipment_context').value = data.equipment_context || '';
|
|
document.getElementById('procedure_context').value = data.procedure_context || '';
|
|
document.getElementById('staff_info').value = data.staff_info || '';
|
|
document.getElementById('contact_info').value = data.contact_info || '';
|
|
document.getElementById('hermes_instructions').value = data.hermes_instructions || '';
|
|
document.getElementById('excluded_senders').value = data.excluded_senders || '';
|
|
|
|
// Afficher l'aperçu
|
|
updatePreview(data.full_context);
|
|
})
|
|
.catch(error => {
|
|
console.error('Erreur chargement contexte:', error);
|
|
});
|
|
}
|
|
|
|
function updatePreview(context) {
|
|
const preview = document.getElementById('fullContextPreview');
|
|
if (context && context.trim()) {
|
|
preview.textContent = context;
|
|
} else {
|
|
preview.textContent = 'Aucun contexte configuré. Remplissez le formulaire ci-dessous pour définir le contexte utilisé par Hermes.';
|
|
preview.classList.add('text-muted');
|
|
}
|
|
|
|
// Afficher le prompt complet
|
|
updatePromptPreview(context);
|
|
}
|
|
|
|
function updatePromptPreview(gmaoContext) {
|
|
const promptPreview = document.getElementById('promptFullPreview');
|
|
|
|
// Prompt système fixe
|
|
const systemPrompt = `Tu es un assistant pour la GMAO (Gestion de Maintenance Assistée par Ordinaire) d'un établissement scolaire.
|
|
|
|
Analyse cet email et EXTRAIS les informations pour créer une ou plusieurs interventions/tâches.
|
|
|
|
IMPORTANT: Tu DOIS répondre UNIQUEMENT avec un JSON valide, sans texte avant ni après.
|
|
|
|
Types possibles:
|
|
- "curative": Panne, problème, réparation nécessaire (ex: fuite, équipement cassé)
|
|
- "preventive": Maintenance planifiée, contrôle périodique (ex: inspection, entretien)
|
|
- "administrative": Tâche administrative à effectuer (ex: déposer des documents, remplir un formulaire, rappel)
|
|
- "formation": Formation à suivre, session de formation (ex: formation sécurité, recyclage)
|
|
- "information": Information simple, pas d'action requise
|
|
|
|
Actions possibles:
|
|
- "create": Créer une nouvelle intervention/tâche
|
|
- "update": Mettre à jour une intervention existante
|
|
- "inform": Juste informer, pas d'action
|
|
|
|
ATTENTION: Un email peut contenir PLUSIEURS tâches différentes (ex: plusieurs dates, plusieurs actions).
|
|
Dans ce cas, crée une entrée pour chaque tâche identifiée.
|
|
|
|
Format de réponse obligatoire:
|
|
{
|
|
"analyses": [
|
|
{
|
|
"type": "curative|preventive|administrative|formation|information",
|
|
"action": "create|update|inform",
|
|
"confidence": 0.0-1.0,
|
|
"intervention": {
|
|
"titre": "Titre court et descriptif",
|
|
"description": "Description détaillée du problème ou de la tâche",
|
|
"urgence": "basse|normale|haute|urgente",
|
|
"lieu": "Localisation précise (bâtiment, salle, étage...)",
|
|
"equipement": "Équipement concerné si applicable",
|
|
"intervenant_suggere": "Type d'intervenant suggéré (plombier, électricien, etc.)",
|
|
"date_intervention": "YYYY-MM-DD ou null si non spécifiée"
|
|
},
|
|
"date_suggestion": "YYYY-MM-DD ou null",
|
|
"resume_court": "Résumé en une phrase"
|
|
}
|
|
],
|
|
"nb_taches": 1,
|
|
"resume_global": "Résumé de l'email en une phrase"
|
|
}`;
|
|
|
|
let fullPrompt = systemPrompt;
|
|
|
|
if (gmaoContext && gmaoContext.trim()) {
|
|
fullPrompt += '\n\n=== CONTEXTE GMAO DE L\'ÉTABLISSEMENT ===\n' + gmaoContext + '\n=== FIN DU CONTEXTE ===';
|
|
}
|
|
|
|
fullPrompt += '\n\n=== EMAIL À ANALYSER ===\n[Sujet: ...]\n[Corps: ...]\n\n=== PIÈCES JOINTES ===\n[PDF: document.pdf]\n[CONTENU PDF]:\nTexte extrait du PDF automatiquement...\n=== FIN DES PIÈCES JOINTES ===\n=== FIN DE L\'EMAIL ===\n\nFournis ton analyse:';
|
|
|
|
promptPreview.textContent = fullPrompt;
|
|
}
|
|
|
|
// Soumission du formulaire
|
|
document.getElementById('contextForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const formData = {
|
|
establishment_name: document.getElementById('establishment_name').value,
|
|
establishment_type: document.getElementById('establishment_type').value,
|
|
address: document.getElementById('address').value,
|
|
technical_context: document.getElementById('technical_context').value,
|
|
equipment_context: document.getElementById('equipment_context').value,
|
|
procedure_context: document.getElementById('procedure_context').value,
|
|
staff_info: document.getElementById('staff_info').value,
|
|
contact_info: document.getElementById('contact_info').value,
|
|
hermes_instructions: document.getElementById('hermes_instructions').value,
|
|
excluded_senders: document.getElementById('excluded_senders').value
|
|
};
|
|
|
|
fetch('/outlook/api/context', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(formData)
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
showToast(data.message, 'success');
|
|
updatePreview(data.full_context);
|
|
} else {
|
|
showToast(data.error || 'Erreur lors de la sauvegarde', 'danger');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showToast('Erreur de communication', 'danger');
|
|
console.error('Erreur:', error);
|
|
});
|
|
});
|
|
|
|
function resetForm() {
|
|
if (confirm('Réinitialiser le formulaire ? Les données non sauvegardées seront perdues.')) {
|
|
loadContext();
|
|
}
|
|
}
|
|
|
|
function showToast(message, type) {
|
|
const toast = document.getElementById('saveToast');
|
|
const toastBody = document.getElementById('toastMessage');
|
|
toastBody.textContent = message;
|
|
|
|
toast.classList.remove('bg-success', 'bg-danger', 'text-white');
|
|
if (type === 'success') {
|
|
toast.classList.add('bg-success', 'text-white');
|
|
} else if (type === 'danger') {
|
|
toast.classList.add('bg-danger', 'text-white');
|
|
}
|
|
|
|
const bsToast = new bootstrap.Toast(toast);
|
|
bsToast.show();
|
|
}
|
|
</script>
|
|
{% endblock %} |