Spaces:
Sleeping
Sleeping
import gradio as gr | |
import pandas as pd | |
import matplotlib.pyplot as plt | |
import numpy as np | |
from datetime import datetime | |
import io | |
import base64 | |
from transformers import pipeline | |
from reportlab.lib.pagesizes import letter, A4 | |
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, Table, TableStyle, PageBreak | |
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle | |
from reportlab.lib.units import inch | |
from reportlab.lib import colors | |
from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY, TA_LEFT | |
import tempfile | |
import os | |
import warnings | |
warnings.filterwarnings("ignore") | |
# Initialize the AI model for text generation | |
try: | |
# Using a smaller, efficient model that works well on HF Spaces | |
generator = pipeline( | |
"text-generation", | |
model="microsoft/DialoGPT-medium", | |
pad_token_id=50256 | |
) | |
except Exception as e: | |
print(f"Model loading error: {e}") | |
generator = None | |
def calculate_metrics(quiz_scores, assignment_scores, participation_score): | |
"""Calculate key performance metrics""" | |
quiz_avg = np.mean(quiz_scores) if quiz_scores else 0 | |
assignment_avg = np.mean(assignment_scores) if assignment_scores else 0 | |
overall_avg = (quiz_avg * 0.4 + assignment_avg * 0.4 + participation_score * 0.2) | |
# Determine grade letter | |
if overall_avg >= 97: grade = "A+" | |
elif overall_avg >= 93: grade = "A" | |
elif overall_avg >= 90: grade = "A-" | |
elif overall_avg >= 87: grade = "B+" | |
elif overall_avg >= 83: grade = "B" | |
elif overall_avg >= 80: grade = "B-" | |
elif overall_avg >= 77: grade = "C+" | |
elif overall_avg >= 73: grade = "C" | |
elif overall_avg >= 70: grade = "C-" | |
elif overall_avg >= 67: grade = "D+" | |
elif overall_avg >= 65: grade = "D" | |
else: grade = "F" | |
return { | |
'quiz_avg': round(quiz_avg, 1), | |
'assignment_avg': round(assignment_avg, 1), | |
'overall_avg': round(overall_avg, 1), | |
'grade': grade | |
} | |
def analyze_performance(metrics, quiz_scores, assignment_scores): | |
"""Analyze performance patterns and generate insights""" | |
strengths = [] | |
improvements = [] | |
# Analyze quiz vs assignment performance | |
if metrics['assignment_avg'] > metrics['quiz_avg'] + 5: | |
improvements.append("Quiz performance under timed conditions") | |
strengths.append("Consistent assignment quality and preparation") | |
elif metrics['quiz_avg'] > metrics['assignment_avg'] + 5: | |
strengths.append("Strong performance under pressure") | |
improvements.append("Assignment completion and thoroughness") | |
# Analyze score consistency | |
if quiz_scores: | |
quiz_std = np.std(quiz_scores) | |
if quiz_std < 5: | |
strengths.append("Consistent quiz performance") | |
elif quiz_std > 15: | |
improvements.append("Score consistency and preparation routine") | |
# Overall performance analysis | |
if metrics['overall_avg'] >= 90: | |
strengths.append("Excellent overall academic performance") | |
elif metrics['overall_avg'] >= 80: | |
strengths.append("Strong understanding of course material") | |
else: | |
improvements.append("Fundamental concept mastery") | |
return strengths[:3], improvements[:3] # Limit to top 3 each | |
def generate_ai_insights(student_name, subject, metrics, strengths, improvements): | |
"""Generate AI-powered insights and recommendations""" | |
# Fallback insights if AI model fails | |
fallback_insights = { | |
'strengths_text': f"{student_name} demonstrates solid academic foundation with a {metrics['grade']} average. Strong areas include consistent study habits and course engagement.", | |
'recommendations': [ | |
"Continue maintaining current study schedule and habits", | |
"Focus on areas showing lower performance for improvement", | |
"Seek additional help or resources for challenging topics", | |
"Practice active recall and spaced repetition techniques" | |
] | |
} | |
if not generator: | |
return fallback_insights | |
try: | |
# Create prompts for AI generation | |
strengths_prompt = f"Student {student_name} in {subject} shows strengths in: {', '.join(strengths)}. Explain these strengths briefly:" | |
# Generate insights (simplified for demo) | |
strengths_response = generator( | |
strengths_prompt, | |
max_length=150, | |
num_return_sequences=1, | |
temperature=0.7, | |
pad_token_id=50256 | |
) | |
recommendations = [ | |
f"Focus on improving {improvements[0] if improvements else 'fundamental concepts'}", | |
f"Build upon strength in {strengths[0] if strengths else 'current performance level'}", | |
"Establish regular study schedule with consistent review sessions", | |
"Seek additional practice in areas showing room for growth" | |
] | |
return { | |
'strengths_text': strengths_response[0]['generated_text'][:200] + "...", | |
'recommendations': recommendations | |
} | |
except Exception as e: | |
print(f"AI generation error: {e}") | |
return fallback_insights | |
def create_performance_chart(metrics, save_path=None): | |
"""Create a simple performance visualization""" | |
categories = ['Quiz Average', 'Assignment Average', 'Participation', 'Overall'] | |
scores = [metrics['quiz_avg'], metrics['assignment_avg'], | |
metrics.get('participation', 0), metrics['overall_avg']] | |
fig, ax = plt.subplots(figsize=(10, 6)) | |
bars = ax.bar(categories, scores, color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4']) | |
# Add value labels on bars | |
for bar, score in zip(bars, scores): | |
height = bar.get_height() | |
ax.text(bar.get_x() + bar.get_width()/2., height + 1, | |
f'{score:.1f}%', ha='center', va='bottom', fontweight='bold') | |
ax.set_ylim(0, 100) | |
ax.set_ylabel('Score (%)', fontweight='bold') | |
ax.set_title('Performance Overview', fontsize=16, fontweight='bold', pad=20) | |
ax.grid(axis='y', alpha=0.3) | |
# Color code based on performance | |
for i, (bar, score) in enumerate(zip(bars, scores)): | |
if score >= 90: | |
bar.set_color('#2ECC71') # Green | |
elif score >= 80: | |
bar.set_color('#F39C12') # Orange | |
else: | |
bar.set_color('#E74C3C') # Red | |
plt.tight_layout() | |
# Save chart if path provided | |
if save_path: | |
plt.savefig(save_path, dpi=300, bbox_inches='tight') | |
return fig | |
def create_pdf_report(student_name, subject, time_period, metrics, strengths, improvements, | |
insights, participation_score, additional_notes="", chart_path=None): | |
"""Generate a comprehensive PDF report""" | |
# Create temporary PDF file | |
temp_pdf = tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') | |
pdf_path = temp_pdf.name | |
temp_pdf.close() | |
# Create PDF document | |
doc = SimpleDocTemplate(pdf_path, pagesize=A4, rightMargin=72, leftMargin=72, | |
topMargin=72, bottomMargin=18) | |
# Get styles | |
styles = getSampleStyleSheet() | |
# Custom styles | |
title_style = ParagraphStyle( | |
'CustomTitle', | |
parent=styles['Heading1'], | |
fontSize=24, | |
spaceAfter=30, | |
alignment=TA_CENTER, | |
textColor=colors.darkblue | |
) | |
heading_style = ParagraphStyle( | |
'CustomHeading', | |
parent=styles['Heading2'], | |
fontSize=16, | |
spaceAfter=12, | |
spaceBefore=20, | |
textColor=colors.darkblue | |
) | |
subheading_style = ParagraphStyle( | |
'CustomSubheading', | |
parent=styles['Heading3'], | |
fontSize=14, | |
spaceAfter=8, | |
spaceBefore=12, | |
textColor=colors.darkgreen | |
) | |
body_style = ParagraphStyle( | |
'CustomBody', | |
parent=styles['Normal'], | |
fontSize=11, | |
spaceAfter=6, | |
alignment=TA_JUSTIFY | |
) | |
# Build PDF content | |
story = [] | |
# Title | |
story.append(Paragraph("π Student Performance Report", title_style)) | |
story.append(Spacer(1, 12)) | |
# Student Information Table | |
student_data = [ | |
['Student Name:', student_name], | |
['Subject/Course:', subject], | |
['Reporting Period:', time_period], | |
['Report Generated:', datetime.now().strftime('%B %d, %Y')], | |
['Generated By:', 'Scoreazy AI Agent'] | |
] | |
student_table = Table(student_data, colWidths=[2*inch, 4*inch]) | |
student_table.setStyle(TableStyle([ | |
('BACKGROUND', (0, 0), (0, -1), colors.lightblue), | |
('TEXTCOLOR', (0, 0), (0, -1), colors.darkblue), | |
('ALIGN', (0, 0), (-1, -1), 'LEFT'), | |
('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'), | |
('FONTNAME', (1, 0), (1, -1), 'Helvetica'), | |
('FONTSIZE', (0, 0), (-1, -1), 12), | |
('GRID', (0, 0), (-1, -1), 1, colors.black), | |
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), | |
])) | |
story.append(student_table) | |
story.append(Spacer(1, 20)) | |
# Performance Summary | |
story.append(Paragraph("π Performance Summary", heading_style)) | |
performance_data = [ | |
['Metric', 'Score', 'Grade'], | |
['Overall Performance', f"{metrics['overall_avg']}%", metrics['grade']], | |
['Quiz Average', f"{metrics['quiz_avg']}%", ''], | |
['Assignment Average', f"{metrics['assignment_avg']}%", ''], | |
['Participation Score', f"{participation_score}%", ''] | |
] | |
performance_table = Table(performance_data, colWidths=[2.5*inch, 1.5*inch, 1*inch]) | |
performance_table.setStyle(TableStyle([ | |
('BACKGROUND', (0, 0), (-1, 0), colors.darkblue), | |
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), | |
('ALIGN', (0, 0), (-1, -1), 'CENTER'), | |
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), | |
('FONTNAME', (0, 1), (-1, -1), 'Helvetica'), | |
('FONTSIZE', (0, 0), (-1, -1), 11), | |
('GRID', (0, 0), (-1, -1), 1, colors.black), | |
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), | |
('BACKGROUND', (0, 1), (-1, -1), colors.beige), | |
])) | |
story.append(performance_table) | |
story.append(Spacer(1, 20)) | |
# Add chart if available | |
if chart_path and os.path.exists(chart_path): | |
story.append(Paragraph("π Performance Visualization", heading_style)) | |
chart_img = Image(chart_path, width=6*inch, height=3.6*inch) | |
story.append(chart_img) | |
story.append(Spacer(1, 20)) | |
# Strengths Section | |
story.append(Paragraph("β Identified Strengths", heading_style)) | |
for i, strength in enumerate(strengths, 1): | |
story.append(Paragraph(f"<b>{i}. {strength.title()}</b>", subheading_style)) | |
story.append(Paragraph(f"Student demonstrates consistent performance and understanding in this area, " | |
f"showing mastery of key concepts and skills.", body_style)) | |
story.append(Spacer(1, 8)) | |
# Areas for Improvement | |
story.append(Paragraph("β οΈ Areas for Improvement", heading_style)) | |
for i, improvement in enumerate(improvements, 1): | |
story.append(Paragraph(f"<b>{i}. {improvement.title()}</b>", subheading_style)) | |
story.append(Paragraph(f"This area presents opportunities for growth and enhanced learning outcomes. " | |
f"Focused attention in this area will yield significant improvements.", body_style)) | |
story.append(Spacer(1, 8)) | |
# Recommendations | |
story.append(Paragraph("π― Personalized Recommendations", heading_style)) | |
for i, rec in enumerate(insights['recommendations'], 1): | |
story.append(Paragraph(f"<b>{i}.</b> {rec}", body_style)) | |
story.append(Spacer(1, 6)) | |
# Additional Notes | |
if additional_notes: | |
story.append(Spacer(1, 20)) | |
story.append(Paragraph("π Additional Notes", heading_style)) | |
story.append(Paragraph(additional_notes, body_style)) | |
# Next Steps | |
story.append(Spacer(1, 20)) | |
story.append(Paragraph("π Next Steps", heading_style)) | |
next_steps = [ | |
"Review this report with student and parent/guardian", | |
"Monitor progress on recommended improvement areas over the next 2-3 weeks", | |
"Schedule follow-up assessment to track improvement", | |
"Continue building on identified strengths through advanced challenges", | |
"Implement suggested study strategies and learning techniques" | |
] | |
for step in next_steps: | |
story.append(Paragraph(f"β’ {step}", body_style)) | |
story.append(Spacer(1, 4)) | |
# Footer | |
story.append(Spacer(1, 30)) | |
footer_style = ParagraphStyle( | |
'Footer', | |
parent=styles['Normal'], | |
fontSize=10, | |
alignment=TA_CENTER, | |
textColor=colors.grey | |
) | |
story.append(Paragraph("Report generated by Scoreazy AI Agent | Educational Technology Solutions", footer_style)) | |
story.append(Paragraph("For questions or support, please contact your instructor", footer_style)) | |
# Build PDF | |
doc.build(story) | |
return pdf_path | |
def generate_report(student_name, subject, quiz_scores_str, assignment_scores_str, | |
participation_score, time_period, additional_notes=""): | |
"""Main function to generate the complete student report""" | |
try: | |
# Parse input scores | |
quiz_scores = [float(x.strip()) for x in quiz_scores_str.split(',') if x.strip()] | |
assignment_scores = [float(x.strip()) for x in assignment_scores_str.split(',') if x.strip()] | |
# Validate scores | |
all_scores = quiz_scores + assignment_scores + [participation_score] | |
if any(score < 0 or score > 100 for score in all_scores): | |
return "Error: All scores must be between 0 and 100.", None, None | |
# Calculate metrics | |
metrics = calculate_metrics(quiz_scores, assignment_scores, participation_score) | |
metrics['participation'] = participation_score | |
# Analyze performance | |
strengths, improvements = analyze_performance(metrics, quiz_scores, assignment_scores) | |
# Generate AI insights | |
insights = generate_ai_insights(student_name, subject, metrics, strengths, improvements) | |
# Create temporary file for chart | |
chart_temp = tempfile.NamedTemporaryFile(delete=False, suffix='.png') | |
chart_path = chart_temp.name | |
chart_temp.close() | |
# Create visualization | |
chart = create_performance_chart(metrics, save_path=chart_path) | |
# Generate PDF report | |
pdf_path = create_pdf_report( | |
student_name, subject, time_period, metrics, strengths, improvements, | |
insights, participation_score, additional_notes, chart_path | |
) | |
# Generate report text for display | |
report = f""" | |
# π Student Performance Report | |
**Student Name:** {student_name} | |
**Subject:** {subject} | |
**Reporting Period:** {time_period} | |
**Generated Date:** {datetime.now().strftime('%B %d, %Y')} | |
--- | |
## π Performance Summary | |
- **Overall Grade:** {metrics['grade']} ({metrics['overall_avg']}%) | |
- **Quiz Average:** {metrics['quiz_avg']}% | |
- **Assignment Average:** {metrics['assignment_avg']}% | |
- **Participation Score:** {participation_score}% | |
--- | |
## β Identified Strengths | |
""" | |
for i, strength in enumerate(strengths, 1): | |
report += f"{i}. **{strength.title()}**: Demonstrates consistent performance and understanding in this area.\n" | |
report += "\n## β οΈ Areas for Improvement\n" | |
for i, improvement in enumerate(improvements, 1): | |
report += f"{i}. **{improvement.title()}**: Focus area for enhanced learning outcomes.\n" | |
report += f""" | |
--- | |
## π― Recommendations | |
""" | |
for i, rec in enumerate(insights['recommendations'], 1): | |
report += f"{i}. {rec}\n" | |
if additional_notes: | |
report += f""" | |
--- | |
## π Additional Notes | |
{additional_notes} | |
""" | |
report += """ | |
--- | |
## π Next Steps | |
- Review this report with student and parent/guardian | |
- Monitor progress on recommended improvement areas | |
- Schedule follow-up assessment in 2-3 weeks | |
- Continue building on identified strengths | |
--- | |
*Report generated by Scoreazy AI Agent | Educational Technology Solutions* | |
""" | |
# Clean up temporary chart file | |
try: | |
os.unlink(chart_path) | |
except: | |
pass | |
return report, chart, pdf_path | |
except ValueError as e: | |
return f"Error parsing scores: {str(e)}. Please ensure scores are numbers separated by commas.", None, None | |
except Exception as e: | |
return f"Error generating report: {str(e)}", None, None | |
# Create Gradio interface | |
def create_interface(): | |
with gr.Blocks(title="π Report Generator Bot", theme=gr.themes.Soft()) as app: | |
gr.Markdown(""" | |
# π€ Student Report Generator Bot | |
### Powered by AI | Built for Scoreazy Assignment | |
Generate comprehensive student performance reports with AI-powered insights and recommendations. | |
""") | |
with gr.Row(): | |
with gr.Column(scale=1): | |
gr.Markdown("## π Student Information") | |
student_name = gr.Textbox( | |
label="Student Name", | |
placeholder="Enter student's full name", | |
value="Sarah Johnson" | |
) | |
subject = gr.Textbox( | |
label="Subject/Course", | |
placeholder="e.g., Mathematics, Science, English", | |
value="Mathematics" | |
) | |
time_period = gr.Textbox( | |
label="Time Period", | |
placeholder="e.g., Quarter 1, Week 1-4, Month 1", | |
value="Quarter 1" | |
) | |
with gr.Column(scale=1): | |
gr.Markdown("## π Performance Data") | |
quiz_scores = gr.Textbox( | |
label="Quiz Scores (comma-separated)", | |
placeholder="85, 78, 92, 88", | |
value="85, 78, 92, 88, 90" | |
) | |
assignment_scores = gr.Textbox( | |
label="Assignment Scores (comma-separated)", | |
placeholder="90, 87, 93, 89", | |
value="90, 87, 93, 89, 91" | |
) | |
participation_score = gr.Slider( | |
label="Participation Score", | |
minimum=0, | |
maximum=100, | |
value=88, | |
step=1 | |
) | |
additional_notes = gr.Textbox( | |
label="Additional Notes (Optional)", | |
placeholder="Any specific observations or concerns...", | |
lines=3 | |
) | |
generate_btn = gr.Button("π Generate Report", variant="primary", size="lg") | |
with gr.Row(): | |
with gr.Column(scale=2): | |
report_output = gr.Markdown(label="Generated Report") | |
with gr.Column(scale=1): | |
chart_output = gr.Plot(label="Performance Chart") | |
# PDF Download Section | |
with gr.Row(): | |
pdf_output = gr.File(label="π Download PDF Report", visible=False) | |
download_status = gr.Markdown("", visible=False) | |
# Example button | |
def load_example(): | |
return ( | |
"Alex Rodriguez", | |
"Computer Science", | |
"95, 87, 91, 89, 93", | |
"88, 92, 86, 94, 90", | |
85, | |
"Semester 1", | |
"Student shows strong technical aptitude but could benefit from more consistent participation in class discussions." | |
) | |
def generate_and_update(student_name, subject, quiz_scores, assignment_scores, | |
participation_score, time_period, additional_notes): | |
"""Generate report and update interface with PDF""" | |
report, chart, pdf_path = generate_report( | |
student_name, subject, quiz_scores, assignment_scores, | |
participation_score, time_period, additional_notes | |
) | |
if pdf_path and os.path.exists(pdf_path): | |
return ( | |
report, | |
chart, | |
gr.update(value=pdf_path, visible=True), | |
gr.update(value="β **PDF Report Generated Successfully!** Click the download button above to save your report.", visible=True) | |
) | |
else: | |
return ( | |
report, | |
chart, | |
gr.update(visible=False), | |
gr.update(value="β Error generating PDF report." if pdf_path is None else "", visible=bool(report)) | |
) | |
example_btn = gr.Button("π Load Example Data", variant="secondary") | |
example_btn.click( | |
fn=load_example, | |
outputs=[student_name, subject, quiz_scores, assignment_scores, | |
participation_score, time_period, additional_notes] | |
) | |
generate_btn.click( | |
fn=generate_and_update, | |
inputs=[student_name, subject, quiz_scores, assignment_scores, | |
participation_score, time_period, additional_notes], | |
outputs=[report_output, chart_output, pdf_output, download_status] | |
) | |
gr.Markdown(""" | |
--- | |
### π‘ How to Use: | |
1. **Enter student information** and course details | |
2. **Input performance data** - use comma-separated values for multiple scores | |
3. **Add participation score** using the slider | |
4. **Click "Generate Report"** to create AI-powered analysis | |
5. **Review the comprehensive report** with strengths, improvements, and recommendations | |
6. **Download PDF version** for offline viewing and sharing | |
### π§ Features: | |
- β Automated performance analysis | |
- β AI-generated insights and recommendations | |
- β Visual performance charts | |
- β Professional report formatting | |
- β **PDF download with embedded charts** | |
- β Customizable input parameters | |
### π PDF Report Includes: | |
- Complete performance analysis with charts | |
- Professional formatting for printing | |
- All recommendations and insights | |
- Visual performance graphs | |
- Ready for sharing with parents/educators | |
""") | |
return app | |
# Launch the app | |
if __name__ == "__main__": | |
app = create_interface() | |
app.launch(share=True) |