| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Admin Dashboard - SASTRA Chatbot</title> |
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> |
| <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> |
| </head> |
| <body> |
| |
| <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> |
| <div class="container"> |
| <a class="navbar-brand" href="/"> |
| <i class="fas fa-university me-2"></i>SASTRA Admin |
| </a> |
| <div class="navbar-nav ms-auto"> |
| <a href="/" class="nav-link"> |
| <i class="fas fa-robot me-1"></i>Chat |
| </a> |
| <a href="/logout" class="nav-link"> |
| <i class="fas fa-sign-out-alt me-1"></i>Logout |
| </a> |
| </div> |
| </div> |
| </nav> |
|
|
| <div class="container mt-4"> |
| <div class="row"> |
| <div class="col-md-12"> |
| <h2 class="mb-4"> |
| <i class="fas fa-tachometer-alt me-2"></i>Admin Dashboard |
| </h2> |
| </div> |
| </div> |
|
|
| |
| <div class="row mb-4"> |
| <div class="col-md-12"> |
| <div class="card"> |
| <div class="card-header bg-warning text-dark"> |
| <h5 class="mb-0"> |
| <i class="fas fa-retweet me-2"></i>Model Retraining |
| </h5> |
| </div> |
| <div class="card-body"> |
| <div class="alert alert-info"> |
| <i class="fas fa-info-circle me-2"></i> |
| Upload a new Excel file with keyword-response pairs to update the chatbot's knowledge. |
| </div> |
| |
| <form id="retrainForm"> |
| <div class="mb-3"> |
| <label for="trainingFile" class="form-label">Upload Training Data (Excel)</label> |
| <input class="form-control" type="file" id="trainingFile" accept=".xlsx"> |
| <div class="form-text"> |
| Excel file should have columns: "keyword" and "response" |
| </div> |
| </div> |
| |
| <div class="d-flex gap-2"> |
| <button type="submit" class="btn btn-warning" id="retrainBtn"> |
| <i class="fas fa-sync me-2"></i>Retrain Model |
| </button> |
| <a href="/api/download_logs" class="btn btn-outline-primary"> |
| <i class="fas fa-download me-2"></i>Download Logs |
| </a> |
| </div> |
| </form> |
| |
| <div id="retrainResult" class="mt-3"></div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="row"> |
| <div class="col-md-12"> |
| <div class="card"> |
| <div class="card-header bg-success text-white"> |
| <h5 class="mb-0"> |
| <i class="fas fa-chart-bar me-2"></i>Analytics Dashboard |
| </h5> |
| </div> |
| <div class="card-body"> |
| <div class="d-flex justify-content-between mb-3"> |
| <button class="btn btn-outline-success" onclick="refreshAnalytics()"> |
| <i class="fas fa-redo me-2"></i>Refresh Analytics |
| </button> |
| <span class="text-muted">Last updated: <span id="lastUpdate">Never</span></span> |
| </div> |
| |
| |
| <div class="row" id="analyticsCards"> |
| <div class="col-md-3 mb-3"> |
| <div class="card text-center h-100"> |
| <div class="card-body"> |
| <h1 class="display-4 text-primary" id="totalQueries">0</h1> |
| <p class="card-text">Total Queries</p> |
| </div> |
| </div> |
| </div> |
| |
| <div class="col-md-9 mb-3"> |
| <div class="card h-100"> |
| <div class="card-header"> |
| <h6 class="mb-0">Language Distribution</h6> |
| </div> |
| <div class="card-body"> |
| <div class="table-responsive"> |
| <table class="table table-sm" id="langTable"> |
| <tbody> |
| |
| </tbody> |
| </table> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="row mt-3"> |
| <div class="col-md-6 mb-3"> |
| <div class="card h-100"> |
| <div class="card-header"> |
| <h6 class="mb-0">Response Types</h6> |
| </div> |
| <div class="card-body"> |
| <div class="table-responsive"> |
| <table class="table table-sm" id="responseTypeTable"> |
| <tbody> |
| |
| </tbody> |
| </table> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="col-md-6 mb-3"> |
| <div class="card h-100"> |
| <div class="card-header"> |
| <h6 class="mb-0">Recent Questions</h6> |
| </div> |
| <div class="card-body"> |
| <ul class="list-group list-group-flush" id="topQuestions"> |
| |
| </ul> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script> |
| |
| <script> |
| document.getElementById('retrainForm').addEventListener('submit', async function(e) { |
| e.preventDefault(); |
| |
| const fileInput = document.getElementById('trainingFile'); |
| const retrainBtn = document.getElementById('retrainBtn'); |
| const resultDiv = document.getElementById('retrainResult'); |
| |
| if (!fileInput.files.length) { |
| resultDiv.innerHTML = ` |
| <div class="alert alert-warning"> |
| <i class="fas fa-exclamation-triangle me-2"></i> |
| Please select an Excel file to upload. |
| </div> |
| `; |
| return; |
| } |
| |
| const formData = new FormData(); |
| formData.append('file', fileInput.files[0]); |
| |
| retrainBtn.disabled = true; |
| retrainBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Retraining...'; |
| |
| try { |
| const response = await fetch('/api/retrain', { |
| method: 'POST', |
| body: formData |
| }); |
| |
| const data = await response.json(); |
| |
| if (response.ok) { |
| resultDiv.innerHTML = ` |
| <div class="alert alert-success"> |
| <i class="fas fa-check-circle me-2"></i> |
| ${data.message} |
| </div> |
| `; |
| |
| refreshAnalytics(); |
| } else { |
| resultDiv.innerHTML = ` |
| <div class="alert alert-danger"> |
| <i class="fas fa-times-circle me-2"></i> |
| Error: ${data.error || 'Failed to retrain'} |
| </div> |
| `; |
| } |
| } catch (error) { |
| resultDiv.innerHTML = ` |
| <div class="alert alert-danger"> |
| <i class="fas fa-times-circle me-2"></i> |
| Network error: ${error.message} |
| </div> |
| `; |
| } finally { |
| retrainBtn.disabled = false; |
| retrainBtn.innerHTML = '<i class="fas fa-sync me-2"></i>Retrain Model'; |
| } |
| }); |
| |
| async function refreshAnalytics() { |
| try { |
| const response = await fetch('/api/analytics'); |
| const data = await response.json(); |
| |
| if (response.ok) { |
| |
| const now = new Date(); |
| document.getElementById('lastUpdate').textContent = now.toLocaleTimeString(); |
| |
| |
| document.getElementById('totalQueries').textContent = data.total_queries || 0; |
| |
| |
| const langTable = document.getElementById('langTable'); |
| langTable.innerHTML = ''; |
| |
| if (data.language_distribution) { |
| for (const [lang, count] of Object.entries(data.language_distribution)) { |
| const row = document.createElement('tr'); |
| row.innerHTML = ` |
| <td>${getLanguageName(lang)}</td> |
| <td>${count}</td> |
| <td> |
| <div class="progress" style="height: 10px;"> |
| <div class="progress-bar" style="width: ${(count / data.total_queries * 100) || 0}%"></div> |
| </div> |
| </td> |
| `; |
| langTable.appendChild(row); |
| } |
| } |
| |
| |
| const responseTable = document.getElementById('responseTypeTable'); |
| responseTable.innerHTML = ''; |
| |
| if (data.response_types) { |
| for (const [type, count] of Object.entries(data.response_types)) { |
| const row = document.createElement('tr'); |
| const badgeColor = getResponseTypeColor(type); |
| row.innerHTML = ` |
| <td><span class="badge ${badgeColor}">${type.toUpperCase()}</span></td> |
| <td>${count}</td> |
| `; |
| responseTable.appendChild(row); |
| } |
| } |
| |
| |
| const topQuestions = document.getElementById('topQuestions'); |
| topQuestions.innerHTML = ''; |
| |
| if (data.top_questions && data.top_questions.length > 0) { |
| data.top_questions.forEach(question => { |
| const li = document.createElement('li'); |
| li.className = 'list-group-item'; |
| li.textContent = question.length > 50 ? question.substring(0, 50) + '...' : question; |
| topQuestions.appendChild(li); |
| }); |
| } |
| } |
| } catch (error) { |
| console.error('Error refreshing analytics:', error); |
| } |
| } |
| |
| function getLanguageName(code) { |
| const languages = { |
| 'en': 'English', |
| 'ta': 'Tamil', |
| 'te': 'Telugu', |
| 'kn': 'Kannada', |
| 'hi': 'Hindi', |
| 'unknown': 'Unknown' |
| }; |
| return languages[code] || code; |
| } |
| |
| function getResponseTypeColor(type) { |
| const colors = { |
| 'keyword': 'bg-warning', |
| 'rag': 'bg-info', |
| 'llm': 'bg-primary', |
| 'error': 'bg-danger' |
| }; |
| return colors[type] || 'bg-secondary'; |
| } |
| |
| |
| document.addEventListener('DOMContentLoaded', refreshAnalytics); |
| </script> |
| </body> |
| </html> |