|
|
| let currentExpert = null;
|
| let scenariosData = [];
|
| let calibrationData = { experts: [] };
|
| const totalExperts = 14;
|
| const metrics = ['AR', 'AE', 'ESR', 'SDM', 'SM'];
|
|
|
|
|
| document.addEventListener('DOMContentLoaded', function() {
|
| console.log('Initialisation du formulaire d\'évaluation des experts...');
|
| initializeUI();
|
| loadAllData();
|
| });
|
|
|
| function initializeUI() {
|
|
|
| const expertGrid = document.getElementById('expertGrid');
|
| let html = '';
|
|
|
| for (let i = 1; i <= totalExperts; i++) {
|
| html += `
|
| <div class="expert-btn" onclick="selectExpert(${i})">
|
| Expert ${i}
|
| </div>
|
| `;
|
| }
|
|
|
| expertGrid.innerHTML = html;
|
|
|
|
|
| updateProgressText(0);
|
| }
|
|
|
| function updateProgressText(completedCount) {
|
| document.getElementById('progressText').textContent =
|
| `${completedCount}/${totalExperts} experts ont complété leur évaluation`;
|
| document.getElementById('progressFill').style.width = `${(completedCount / totalExperts) * 100}%`;
|
| }
|
|
|
| async function loadAllData() {
|
| showLoading(true);
|
| showInfo('Chargement des données en cours...');
|
|
|
| try {
|
|
|
| const scenariosResponse = await fetch('scenarios_list.json');
|
| if (!scenariosResponse.ok) {
|
| throw new Error('scenarios_list.json non trouvé ou erreur de chargement');
|
| }
|
| const scenariosJson = await scenariosResponse.json();
|
| scenariosData = scenariosJson.scenarios || [];
|
| console.log(`${scenariosData.length} scénarios chargés`);
|
|
|
|
|
| await loadCalibrationData();
|
|
|
|
|
| initializeScenarioList();
|
|
|
|
|
| updateExpertGrid();
|
|
|
| showLoading(false);
|
| showSuccess('Données chargées avec succès');
|
|
|
| } catch (error) {
|
| console.error('Erreur de chargement:', error);
|
| showLoading(false);
|
| showInfo(`Erreur lors du chargement: ${error.message}. Vous pouvez continuer avec des données vides.`);
|
|
|
|
|
| if (scenariosData.length === 0) {
|
| scenariosData = Array.from({length: 16}, (_, i) => ({
|
| id: i + 1,
|
| title: `Scénario ${i + 1}`
|
| }));
|
| initializeScenarioList();
|
| }
|
| updateExpertGrid();
|
| }
|
| }
|
|
|
| async function loadCalibrationData() {
|
| try {
|
|
|
| const savedData = localStorage.getItem('calibration_trust_data');
|
| if (savedData) {
|
| calibrationData = JSON.parse(savedData);
|
| console.log('Données de calibration chargées depuis localStorage:', calibrationData);
|
| return;
|
| }
|
|
|
|
|
| try {
|
| const response = await fetch('calibration-trust.json');
|
| if (response.ok) {
|
| calibrationData = await response.json();
|
| console.log('Données de calibration chargées depuis fichier:', calibrationData);
|
|
|
|
|
| localStorage.setItem('calibration_trust_data', JSON.stringify(calibrationData));
|
| } else {
|
| createEmptyCalibrationData();
|
| }
|
| } catch (fileError) {
|
| console.log('Fichier calibration-trust.json non trouvé, création de nouvelles données');
|
| createEmptyCalibrationData();
|
| }
|
|
|
| } catch (error) {
|
| console.error('Erreur lors du chargement des données de calibration:', error);
|
| createEmptyCalibrationData();
|
| }
|
|
|
|
|
| ensureAllExpertsExist();
|
| }
|
|
|
| function createEmptyCalibrationData() {
|
| calibrationData = { experts: [] };
|
| for (let i = 1; i <= totalExperts; i++) {
|
| calibrationData.experts.push({
|
| expert_id: i,
|
| evaluations: [],
|
| last_updated: null
|
| });
|
| }
|
| console.log('Nouvelles données de calibration créées');
|
| }
|
|
|
| function ensureAllExpertsExist() {
|
| for (let i = 1; i <= totalExperts; i++) {
|
| const existingExpert = calibrationData.experts.find(e => e.expert_id === i);
|
| if (!existingExpert) {
|
| calibrationData.experts.push({
|
| expert_id: i,
|
| evaluations: [],
|
| last_updated: null
|
| });
|
| }
|
| }
|
|
|
|
|
| calibrationData.experts.sort((a, b) => a.expert_id - b.expert_id);
|
| }
|
|
|
| function updateExpertGrid() {
|
| const expertGrid = document.getElementById('expertGrid');
|
| let html = '';
|
|
|
| let completedCount = 0;
|
|
|
| for (let i = 1; i <= totalExperts; i++) {
|
| const expert = calibrationData.experts.find(e => e.expert_id === i);
|
| const isCompleted = expert && expert.evaluations &&
|
| expert.evaluations.length === scenariosData.length &&
|
| expert.evaluations.every(e =>
|
| metrics.every(m => e[m] !== null && e[m] !== undefined)
|
| );
|
| const isCurrent = currentExpert === i;
|
|
|
| if (isCompleted) completedCount++;
|
|
|
| html += `
|
| <div class="expert-btn ${isCurrent ? 'active' : ''} ${isCompleted ? 'completed' : ''}"
|
| onclick="selectExpert(${i})">
|
| Expert ${i}
|
| ${isCompleted ? '✓' : ''}
|
| </div>
|
| `;
|
| }
|
|
|
| expertGrid.innerHTML = html;
|
|
|
|
|
| updateProgressText(completedCount);
|
| }
|
|
|
| function initializeScenarioList() {
|
| const scenarioList = document.getElementById('scenarioList');
|
| let html = '';
|
|
|
| scenariosData.forEach((scenario, index) => {
|
| html += `
|
| <div class="scenario-item" data-scenario-id="${scenario.id}">
|
| <div class="scenario-title">
|
| <span>${scenario.title || `Scénario ${scenario.id}`}</span>
|
| <span class="scenario-id">S${scenario.id}</span>
|
| </div>
|
| <div class="metrics-grid">
|
| ${metrics.map(metric => `
|
| <div class="metric-input">
|
| <label>${metric}</label>
|
| <select id="metric-${scenario.id}-${metric}"
|
| onchange="updateMetricColor(this, ${scenario.id}, '${metric}')"
|
| data-scenario="${scenario.id}"
|
| data-metric="${metric}">
|
| <option value="">--</option>
|
| <option value="1">1 ★</option>
|
| <option value="2">2 ★★</option>
|
| <option value="3">3 ★★★</option>
|
| <option value="4">4 ★★★★</option>
|
| <option value="5">5 ★★★★★</option>
|
| </select>
|
| </div>
|
| `).join('')}
|
| </div>
|
| </div>
|
| `;
|
| });
|
|
|
| scenarioList.innerHTML = html;
|
| }
|
|
|
|
|
| function selectExpert(expertId) {
|
| currentExpert = expertId;
|
| document.getElementById('currentExpert').textContent = expertId;
|
|
|
|
|
| const expertButtons = document.querySelectorAll('.expert-btn');
|
| expertButtons.forEach(btn => {
|
| btn.classList.remove('active');
|
| if (btn.textContent.includes(`Expert ${expertId}`)) {
|
| btn.classList.add('active');
|
| }
|
| });
|
|
|
|
|
| document.getElementById('evaluationForm').style.display = 'block';
|
| document.getElementById('summaryPanel').classList.remove('active');
|
|
|
|
|
| loadExpertEvaluations(expertId);
|
| }
|
|
|
| function loadExpertEvaluations(expertId) {
|
| const expert = calibrationData.experts.find(e => e.expert_id === expertId);
|
|
|
| if (!expert || !expert.evaluations || expert.evaluations.length === 0) {
|
|
|
| resetForm();
|
| showInfo(`Aucune évaluation trouvée pour l'expert ${expertId}. Commencez une nouvelle évaluation.`);
|
| return;
|
| }
|
|
|
|
|
| let loadedCount = 0;
|
| expert.evaluations.forEach(evaluation => {
|
| metrics.forEach(metric => {
|
| const selectElement = document.getElementById(`metric-${evaluation.scenario_id}-${metric}`);
|
| if (selectElement && evaluation[metric]) {
|
| selectElement.value = evaluation[metric];
|
| updateMetricColor(selectElement, evaluation.scenario_id, metric);
|
| loadedCount++;
|
| }
|
| });
|
| });
|
|
|
| showSuccess(`${loadedCount} évaluations chargées pour l'expert ${expertId}`);
|
| }
|
|
|
|
|
| function updateMetricColor(selectElement, scenarioId, metric) {
|
| const value = parseInt(selectElement.value);
|
| selectElement.className = '';
|
|
|
| if (value >= 4) {
|
| selectElement.classList.add('high');
|
| } else if (value >= 3) {
|
| selectElement.classList.add('medium');
|
| } else if (value >= 1) {
|
| selectElement.classList.add('low');
|
| }
|
| }
|
|
|
| function resetForm() {
|
| if (!currentExpert) {
|
| showInfo('Veuillez d\'abord sélectionner un expert.');
|
| return;
|
| }
|
|
|
|
|
| const selectElements = document.querySelectorAll('.metric-input select');
|
| selectElements.forEach(select => {
|
| select.value = '';
|
| select.className = '';
|
| });
|
|
|
| showInfo('Formulaire réinitialisé. Tous les champs sont vides.');
|
| }
|
|
|
| async function saveEvaluation() {
|
| if (!currentExpert) {
|
| showInfo('Veuillez d\'abord sélectionner un expert.');
|
| return;
|
| }
|
|
|
|
|
| const evaluations = [];
|
| let allFilled = true;
|
| let emptyCount = 0;
|
| let filledCount = 0;
|
|
|
| scenariosData.forEach(scenario => {
|
| const evaluation = { scenario_id: scenario.id };
|
|
|
| metrics.forEach(metric => {
|
| const selectElement = document.getElementById(`metric-${scenario.id}-${metric}`);
|
| const value = selectElement ? parseInt(selectElement.value) : null;
|
|
|
| if (value && !isNaN(value) && value >= 1 && value <= 5) {
|
| evaluation[metric] = value;
|
| filledCount++;
|
| } else {
|
| evaluation[metric] = null;
|
| allFilled = false;
|
| emptyCount++;
|
| }
|
| });
|
|
|
| evaluations.push(evaluation);
|
| });
|
|
|
| if (filledCount === 0) {
|
| showInfo('Aucune évaluation saisie. Veuillez remplir au moins un champ.');
|
| return;
|
| }
|
|
|
| if (!allFilled) {
|
| const confirmSave = confirm(`Attention : ${emptyCount} champs sur ${scenariosData.length * 5} ne sont pas remplis. Voulez-vous quand même sauvegarder ?`);
|
| if (!confirmSave) {
|
| return;
|
| }
|
| }
|
|
|
|
|
| const expertIndex = calibrationData.experts.findIndex(e => e.expert_id === currentExpert);
|
| if (expertIndex !== -1) {
|
| calibrationData.experts[expertIndex].evaluations = evaluations;
|
| calibrationData.experts[expertIndex].last_updated = new Date().toISOString();
|
| }
|
|
|
|
|
| localStorage.setItem('calibration_trust_data', JSON.stringify(calibrationData));
|
|
|
|
|
| await saveCalibrationToFile();
|
|
|
|
|
| updateExpertGrid();
|
| showSummary();
|
|
|
| showSuccess(`Évaluations sauvegardées pour l'expert ${currentExpert} ! ${filledCount} valeurs enregistrées.`);
|
| }
|
|
|
| async function saveCalibrationToFile() {
|
| try {
|
|
|
| const dataStr = JSON.stringify(calibrationData, null, 2);
|
| const dataBlob = new Blob([dataStr], { type: 'application/json' });
|
|
|
|
|
| const downloadLink = document.createElement('a');
|
| downloadLink.href = URL.createObjectURL(dataBlob);
|
| downloadLink.download = 'calibration-trust.json';
|
|
|
|
|
| document.body.appendChild(downloadLink);
|
| downloadLink.click();
|
| document.body.removeChild(downloadLink);
|
|
|
| console.log('Données de calibration sauvegardées dans calibration-trust.json');
|
|
|
| } catch (error) {
|
| console.error('Erreur lors de la sauvegarde du fichier:', error);
|
| showInfo('Les données ont été sauvegardées localement mais une erreur est survenue lors de l\'export du fichier.');
|
| }
|
| }
|
|
|
|
|
| function showSummary() {
|
| if (!currentExpert) return;
|
|
|
| const expert = calibrationData.experts.find(e => e.expert_id === currentExpert);
|
| if (!expert || !expert.evaluations || expert.evaluations.length === 0) {
|
| showInfo('Aucune évaluation à afficher.');
|
| return;
|
| }
|
|
|
|
|
| const stats = calculateExpertStatistics(expert);
|
|
|
|
|
| const summaryGrid = document.getElementById('summaryGrid');
|
|
|
| summaryGrid.innerHTML = `
|
| <div class="summary-card">
|
| <h4>Scénarios évalués</h4>
|
| <div class="summary-value">${stats.completedScenarios}/${scenariosData.length}</div>
|
| <div class="summary-label">${stats.completionRate}% complétés</div>
|
| </div>
|
| <div class="summary-card">
|
| <h4>Moyenne AR</h4>
|
| <div class="summary-value">${stats.averages.AR.toFixed(2)}</div>
|
| <div class="summary-label">/ 5.0</div>
|
| </div>
|
| <div class="summary-card">
|
| <h4>Moyenne AE</h4>
|
| <div class="summary-value">${stats.averages.AE.toFixed(2)}</div>
|
| <div class="summary-label">/ 5.0</div>
|
| </div>
|
| <div class="summary-card">
|
| <h4>Moyenne ESR</h4>
|
| <div class="summary-value">${stats.averages.ESR.toFixed(2)}</div>
|
| <div class="summary-label">/ 5.0</div>
|
| </div>
|
| <div class="summary-card">
|
| <h4>Moyenne SDM</h4>
|
| <div class="summary-value">${stats.averages.SDM.toFixed(2)}</div>
|
| <div class="summary-label">/ 5.0</div>
|
| </div>
|
| <div class="summary-card">
|
| <h4>Moyenne SM</h4>
|
| <div class="summary-value">${stats.averages.SM.toFixed(2)}</div>
|
| <div class="summary-label">/ 5.0</div>
|
| </div>
|
| <div class="summary-card">
|
| <h4>Indice Global</h4>
|
| <div class="summary-value">${stats.globalIndex.toFixed(2)}</div>
|
| <div class="summary-label">/ 5.0</div>
|
| </div>
|
| <div class="summary-card">
|
| <h4>Dernière mise à jour</h4>
|
| <div class="summary-value">${stats.lastUpdated ? new Date(stats.lastUpdated).toLocaleDateString('fr-FR') : 'N/A'}</div>
|
| <div class="summary-label">Date</div>
|
| </div>
|
| `;
|
|
|
|
|
| document.getElementById('summaryPanel').classList.add('active');
|
| document.getElementById('evaluationForm').style.display = 'none';
|
| }
|
|
|
| function calculateExpertStatistics(expert) {
|
| const evaluations = expert.evaluations;
|
|
|
|
|
| const completedScenarios = evaluations.filter(e =>
|
| metrics.every(m => e[m] !== null && e[m] !== undefined)
|
| ).length;
|
|
|
| const completionRate = scenariosData.length > 0
|
| ? ((completedScenarios / scenariosData.length) * 100).toFixed(1)
|
| : '0.0';
|
|
|
|
|
| const averages = {};
|
| metrics.forEach(metric => {
|
| const values = evaluations
|
| .map(e => e[metric])
|
| .filter(v => v !== null && v !== undefined && !isNaN(v));
|
|
|
| averages[metric] = values.length > 0
|
| ? values.reduce((sum, val) => sum + val, 0) / values.length
|
| : 0;
|
| });
|
|
|
|
|
| const metricValues = Object.values(averages).filter(v => v > 0);
|
| const globalIndex = metricValues.length > 0
|
| ? metricValues.reduce((sum, val) => sum + val, 0) / metricValues.length
|
| : 0;
|
|
|
| return {
|
| completedScenarios,
|
| completionRate,
|
| averages,
|
| globalIndex,
|
| lastUpdated: expert.last_updated
|
| };
|
| }
|
|
|
| function showConsensusAnalysis() {
|
|
|
| saveCalibrationToFile();
|
|
|
|
|
| window.location.href = 'consensus_building.html?fromEvaluation=true';
|
| }
|
|
|
| function exportCalibrationData() {
|
| saveCalibrationToFile();
|
| showSuccess('Données de calibration exportées dans calibration-trust.json !');
|
| }
|
|
|
|
|
| function showLoading(show) {
|
| document.getElementById('loading').style.display = show ? 'block' : 'none';
|
| }
|
|
|
| function showInfo(message) {
|
| const alertBox = document.getElementById('infoAlert');
|
| const messageElement = document.getElementById('infoMessage');
|
|
|
| if (messageElement) {
|
| messageElement.textContent = message;
|
| }
|
|
|
| if (alertBox) {
|
| alertBox.style.display = 'block';
|
|
|
| setTimeout(() => {
|
| alertBox.style.display = 'none';
|
| }, 5000);
|
| }
|
| }
|
|
|
| function showSuccess(message) {
|
| const alertBox = document.getElementById('successAlert');
|
| const messageElement = document.getElementById('successMessage');
|
|
|
| if (messageElement) {
|
| messageElement.textContent = message;
|
| }
|
|
|
| if (alertBox) {
|
| alertBox.style.display = 'block';
|
|
|
| setTimeout(() => {
|
| alertBox.style.display = 'none';
|
| }, 5000);
|
| }
|
| }
|
|
|
|
|
| window.resetForm = resetForm;
|
| window.saveEvaluation = saveEvaluation;
|
| window.selectExpert = selectExpert;
|
| window.updateMetricColor = updateMetricColor;
|
| window.showConsensusAnalysis = showConsensusAnalysis;
|
| window.exportCalibrationData = exportCalibrationData; |