|
| 1 | +# analytics/analytics_engine.py |
| 2 | +import statistics |
| 3 | +from database.db_manager import DatabaseManager |
| 4 | +from config import MAX_GPA_CHART_STUDENTS |
| 5 | + |
| 6 | +class AnalyticsEngine: |
| 7 | + def __init__(self, db: DatabaseManager): |
| 8 | + self.db = db |
| 9 | + |
| 10 | + def get_dashboard_stats(self): |
| 11 | + return self.db.get_stats() |
| 12 | + |
| 13 | + def get_gpa_chart_data(self): |
| 14 | + students = self.db.get_all_students() |
| 15 | + # Limit number of students shown in the GPA chart to avoid visual saturation |
| 16 | + max_n = MAX_GPA_CHART_STUDENTS if MAX_GPA_CHART_STUDENTS and MAX_GPA_CHART_STUDENTS > 0 else len(students) |
| 17 | + students = students[:max_n] |
| 18 | + names = [s['name'].split()[0] for s in students] |
| 19 | + gpas = [s['gpa'] for s in students] |
| 20 | + return names, gpas |
| 21 | + |
| 22 | + def get_subject_chart_data(self): |
| 23 | + avgs = self.db.get_subject_averages() |
| 24 | + labels = ['Attendance', 'Quiz', 'Assignment', 'Midterm'] |
| 25 | + values = [avgs['attendance'], avgs['quiz'], avgs['assignment'], avgs['midterm']] |
| 26 | + return labels, values |
| 27 | + |
| 28 | + def get_risk_breakdown(self): |
| 29 | + students = self.db.get_all_students() |
| 30 | + low = sum(1 for s in students if s['risk_level'] == 'Low') |
| 31 | + med = sum(1 for s in students if s['risk_level'] == 'Medium') |
| 32 | + high = sum(1 for s in students if s['risk_level'] == 'High') |
| 33 | + return {'Low': low, 'Medium': med, 'High': high} |
| 34 | + |
| 35 | + def get_gpa_bins(self): |
| 36 | + gpas = self.db.get_gpa_distribution() |
| 37 | + bins = {'<2.0': 0, '2.0–2.5': 0, '2.5–3.0': 0, '3.0–3.5': 0, '3.5–4.0': 0} |
| 38 | + for g in gpas: |
| 39 | + if g < 2.0: bins['<2.0'] += 1 |
| 40 | + elif g < 2.5: bins['2.0–2.5'] += 1 |
| 41 | + elif g < 3.0: bins['2.5–3.0'] += 1 |
| 42 | + elif g < 3.5: bins['3.0–3.5'] += 1 |
| 43 | + else: bins['3.5–4.0'] += 1 |
| 44 | + return bins |
| 45 | + |
| 46 | + def get_performance_insights(self): |
| 47 | + stats = self.get_dashboard_stats() |
| 48 | + insights = [] |
| 49 | + avg_gpa = stats.get('avg_gpa', 0) or 0 |
| 50 | + avg_att = stats.get('avg_att', 0) or 0 |
| 51 | + at_risk = stats.get('at_risk', 0) or 0 |
| 52 | + if avg_gpa >= 3.3: |
| 53 | + insights.append({'icon': '🌟', 'text': f'Average GPA {avg_gpa:.2f} — class is performing well', 'color': '#10B981'}) |
| 54 | + elif avg_gpa < 2.8: |
| 55 | + insights.append({'icon': '⚠️', 'text': f'Average GPA {avg_gpa:.2f} — intervention recommended', 'color': '#EF4444'}) |
| 56 | + if avg_att >= 85: |
| 57 | + insights.append({'icon': '✅', 'text': f'Attendance at {avg_att:.1f}% — excellent engagement', 'color': '#10B981'}) |
| 58 | + elif avg_att < 75: |
| 59 | + insights.append({'icon': '📅', 'text': f'Attendance at {avg_att:.1f}% — needs improvement', 'color': '#F59E0B'}) |
| 60 | + if at_risk > 0: |
| 61 | + insights.append({'icon': '🚨', 'text': f'{at_risk} student(s) at high risk — require attention', 'color': '#EF4444'}) |
| 62 | + if not insights: |
| 63 | + insights.append({'icon': '📊', 'text': 'All metrics within normal range', 'color': '#4F6EF7'}) |
| 64 | + return insights |
0 commit comments