| {% extends "base.html" %} |
|
|
| {% block title %}Thống kê cá nhân - Student Feedback Analysis{% endblock %} |
| {% block page_title %}Thống kê feedback của tôi{% endblock %} |
| {% block footer %}{% endblock %} |
|
|
| {% block extra_head %} |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> |
| <style> |
| |
| .table thead th { color: #374151; } |
| .table tbody td { color: #374151; } |
| </style> |
| {% endblock %} |
|
|
| {% block content %} |
| |
| <div class="row mb-4"> |
| <div class="col-md-12"> |
| <div class="card text-center"> |
| <div class="card-body"> |
| <i class="fas fa-comments fa-3x mb-3" style="color: #10B981 !important;"></i> |
| <h3 class="card-title">{{ total_feedbacks }}</h3> |
| <p class="card-text">Tổng số feedback của tôi</p> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="row mb-4"> |
| <div class="col-md-6"> |
| <div class="card"> |
| <div class="card-header"> |
| <h5 class="mb-0"><i class="fas fa-chart-pie me-2" style="color: #F59E0B !important;"></i>Phân bố Sentiment</h5> |
| </div> |
| <div class="card-body"> |
| <canvas id="sentimentChart" width="400" height="200"></canvas> |
| </div> |
| </div> |
| </div> |
| <div class="col-md-6"> |
| <div class="card"> |
| <div class="card-header"> |
| <h5 class="mb-0"><i class="fas fa-chart-bar me-2" style="color: #F3F4F6 !important;"></i>Phân bố Topic</h5> |
| </div> |
| <div class="card-body"> |
| <canvas id="topicChart" width="400" height="200"></canvas> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="card mb-4"> |
| <div class="card-header"> |
| <h5 class="mb-0"><i class="fas fa-history me-2" style="color: #EF4444 !important;"></i>Feedback gần nhất của tôi</h5> |
| </div> |
| <div class="card-body"> |
| {% if recent_feedbacks %} |
| <div class="table-responsive"> |
| <table class="table table-striped"> |
| <thead> |
| <tr> |
| <th>ID</th> |
| <th>Feedback</th> |
| <th>Sentiment</th> |
| <th>Topic</th> |
| <th>Thời gian</th> |
| </tr> |
| </thead> |
| <tbody> |
| {% for feedback in recent_feedbacks %} |
| <tr> |
| <td>{{ feedback.id }}</td> |
| <td> |
| <div style="max-width: 300px; overflow: hidden; text-overflow: ellipsis;"> |
| {{ feedback.text }} |
| </div> |
| </td> |
| <td> |
| <span class="badge bg-{{ 'success' if feedback.sentiment == 'positive' else 'warning' if feedback.sentiment == 'neutral' else 'danger' }}"> |
| {{ feedback.sentiment }} |
| </span> |
| </td> |
| <td> |
| <span class="badge bg-secondary">{{ feedback.topic }}</span> |
| </td> |
| <td>{{ utc_to_vietnam_time(feedback.created_at).strftime('%d/%m/%Y %H:%M') }}</td> |
| </tr> |
| {% endfor %} |
| </tbody> |
| </table> |
| </div> |
| {% else %} |
| <p class="text-muted text-center">Chưa có feedback nào.</p> |
| {% endif %} |
| </div> |
| </div> |
|
|
| |
| <div class="row"> |
| <div class="col-md-12"> |
| <div class="card"> |
| <div class="card-header"> |
| <h5 class="mb-0"><i class="fas fa-chart-line me-2" style="color: #06B6D4 !important;"></i>Feedback theo ngày (30 ngày gần nhất)</h5> |
| </div> |
| <div class="card-body"> |
| <canvas id="dailyChart" width="400" height="200"></canvas> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="sentiment-data" style="display: none;">{{ sentiment_stats | tojson | safe }}</div> |
| <div id="topic-data" style="display: none;">{{ topic_stats | tojson | safe }}</div> |
| <div id="daily-data" style="display: none;">{{ daily_stats | tojson | safe }}</div> |
|
|
| {% endblock %} |
|
|
| {% block extra_scripts %} |
| <script> |
| |
| document.addEventListener('DOMContentLoaded', function() { |
| |
| const sentimentData = JSON.parse(document.getElementById('sentiment-data').textContent); |
| const topicData = JSON.parse(document.getElementById('topic-data').textContent); |
| const dailyData = JSON.parse(document.getElementById('daily-data').textContent); |
| |
| |
| const sentimentLabels = sentimentData.map(item => item.sentiment); |
| const sentimentCounts = sentimentData.map(item => item.count); |
| new Chart(document.getElementById('sentimentChart'), { |
| type: 'doughnut', |
| data: { |
| labels: sentimentLabels, |
| datasets: [{ |
| data: sentimentCounts, |
| backgroundColor: ['#28a745', '#ffc107', '#dc3545'] |
| }] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| plugins: { |
| legend: { |
| position: 'bottom', |
| labels: { |
| generateLabels: function(chart) { |
| const data = chart.data; |
| if (data.labels.length && data.datasets.length) { |
| const dataset = data.datasets[0]; |
| const total = dataset.data.reduce((a, b) => a + b, 0); |
| return data.labels.map((label, i) => { |
| const value = dataset.data[i]; |
| const percentage = ((value / total) * 100).toFixed(1); |
| return { |
| text: `${label}: ${value} (${percentage}%)`, |
| fillStyle: dataset.backgroundColor[i], |
| strokeStyle: dataset.backgroundColor[i], |
| lineWidth: 1, |
| hidden: false, |
| index: i |
| }; |
| }); |
| } |
| return []; |
| } |
| } |
| } |
| } |
| } |
| }); |
| |
| |
| const topicLabels = topicData.map(item => item.topic); |
| const topicCounts = topicData.map(item => item.count); |
| new Chart(document.getElementById('topicChart'), { |
| type: 'bar', |
| data: { |
| labels: topicLabels, |
| datasets: [{ |
| data: topicCounts, |
| backgroundColor: ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6'] |
| }] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| scales: { y: { beginAtZero: true } }, |
| plugins: { |
| legend: { |
| display: false |
| } |
| } |
| } |
| }); |
| |
| |
| const dailyLabels = dailyData.map(item => item.date); |
| const dailyCounts = dailyData.map(item => item.count); |
| new Chart(document.getElementById('dailyChart'), { |
| type: 'line', |
| data: { |
| labels: dailyLabels, |
| datasets: [{ |
| label: 'Feedback/ngày', |
| data: dailyCounts, |
| borderColor: '#28a745', |
| backgroundColor: 'rgba(40, 167, 69, 0.1)', |
| tension: 0.4 |
| }] |
| }, |
| options: { |
| responsive: true, |
| maintainAspectRatio: false, |
| scales: { y: { beginAtZero: true } } |
| } |
| }); |
| }); |
| </script> |
| {% endblock %} |
|
|