SEAAITUTOR / app.py
AptlyDigital's picture
Update app.py
2aeb870 verified
"""
SEA Prep Pro - AI-Powered Tutor ๐Ÿ‡น๐Ÿ‡น
FULLY FREE VERSION - No API Keys Required!
"""
import gradio as gr
import sqlite3
import json
import os
import re
import random
import math
import threading
from datetime import datetime
# ==================== FREE AI TUTOR ====================
class FreeAITutor:
"""Free AI tutor using rule-based generation"""
def __init__(self):
self.math_templates = {
"Fractions": [
"Simplify the fraction {num}/{den}.",
"Add {a}/{b} + {c}/{d}.",
"Subtract {a}/{b} - {c}/{d}.",
"What is {num}/{den} of {whole}?",
"Convert {decimal} to a fraction."
],
"Geometry": [
"Find the area of a rectangle with length {l}cm and width {w}cm.",
"Calculate the perimeter of a square with side {s}cm.",
"Find the area of a triangle with base {b}cm and height {h}cm.",
"Calculate the circumference of a circle with radius {r}cm (use ฯ€=3.14).",
"Find the volume of a cube with side {s}cm."
]
}
self.english_templates = {
"Grammar": [
"Correct the sentence: '{sentence}'",
"Choose the correct word: '{sentence}'"
],
"Vocabulary": [
"What is a synonym for '{word}'?",
"What is the antonym of '{word}'?"
]
}
def generate_math_question(self, topic, difficulty):
"""Generate math question"""
templates = self.math_templates.get(topic, ["Practice {topic}."])
template = random.choice(templates)
# Generate values based on difficulty
max_val = {1: 10, 2: 20, 3: 50, 4: 100, 5: 200}[difficulty]
values = {
'num': random.randint(1, max_val),
'den': random.randint(2, max_val),
'a': random.randint(1, max_val),
'b': random.randint(2, max_val),
'c': random.randint(1, max_val),
'd': random.randint(2, max_val),
'whole': random.randint(10, 100),
'decimal': round(random.uniform(0.1, 0.9), 2),
'l': random.randint(5, 20),
'w': random.randint(3, 15),
's': random.randint(4, 12),
'r': random.randint(2, 10),
'h': random.randint(3, 12),
'b': random.randint(4, 16),
'topic': topic
}
question = template.format(**values)
answer = self._calculate_answer(question, values)
return question, answer
def _calculate_answer(self, question, values):
"""Calculate answer for math question"""
try:
if "Simplify the fraction" in question:
num, den = values['num'], values['den']
gcd = math.gcd(num, den)
return f"{num//gcd}/{den//gcd}"
elif "area of a rectangle" in question:
return f"{values['l'] * values['w']} cmยฒ"
elif "perimeter of a square" in question:
return f"{4 * values['s']} cm"
elif "area of a triangle" in question:
return f"{0.5 * values['b'] * values['h']} cmยฒ"
elif "circumference of a circle" in question:
return f"{2 * 3.14 * values['r']:.2f} cm"
elif "volume of a cube" in question:
return f"{values['s'] ** 3} cmยณ"
elif "Add" in question and "/" in question:
a, b, c, d = values['a'], values['b'], values['c'], values['d']
lcm = b * d // math.gcd(b, d)
num = a * (lcm // b) + c * (lcm // d)
gcd = math.gcd(num, lcm)
return f"{num//gcd}/{lcm//gcd}"
else:
return "Calculate step by step"
except Exception:
return "Practice this concept"
def evaluate_answer(self, student_answer, correct_answer):
"""Evaluate student's answer"""
student = str(student_answer).strip().lower()
correct = str(correct_answer).strip().lower()
if student == correct:
return {"correct": True, "score": 1.0, "feedback": "โœ… Correct! Well done!"}
# Partial credit for showing work
if len(student) > 10:
return {"correct": False, "score": 0.5, "feedback": "โš ๏ธ Good attempt. Keep practicing!"}
return {"correct": False, "score": 0.0, "feedback": "โŒ Incorrect. Try again!"}
# ==================== DATABASE ====================
class ThreadLocal(threading.local):
def __init__(self):
self.connections = {}
thread_local = ThreadLocal()
def get_db_connection(db_name="questions"):
"""Get database connection"""
if not hasattr(thread_local, 'connections'):
thread_local.connections = {}
if db_name not in thread_local.connections:
conn = sqlite3.connect(f"/tmp/{db_name}.db", check_same_thread=False)
thread_local.connections[db_name] = conn
return thread_local.connections[db_name]
def init_databases():
"""Initialize databases"""
# Questions DB
conn = get_db_connection("questions")
cur = conn.cursor()
cur.execute('''
CREATE TABLE IF NOT EXISTS questions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
subject TEXT,
topic TEXT,
question_text TEXT,
difficulty INTEGER,
correct_answer TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# Student DB
conn = get_db_connection("students")
cur = conn.cursor()
cur.execute('''
CREATE TABLE IF NOT EXISTS student_progress (
id INTEGER PRIMARY KEY AUTOINCREMENT,
student_id TEXT,
question_id INTEGER,
student_answer TEXT,
correct BOOLEAN,
score REAL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
cur.execute('''
CREATE TABLE IF NOT EXISTS student_stats (
student_id TEXT PRIMARY KEY,
total_questions INTEGER DEFAULT 0,
correct_answers INTEGER DEFAULT 0,
total_score REAL DEFAULT 0,
level INTEGER DEFAULT 1
)
''')
conn.commit()
# ==================== MAIN APP ====================
class SEAITutor:
def __init__(self):
self.ai = FreeAITutor()
init_databases()
print("๐Ÿš€ SEA Prep Pro - Free AI Tutor Started!")
self.subjects = {
"Mathematics": {
"topics": ["Fractions", "Geometry", "Algebra", "Word Problems", "Percentages"],
"icon": "๐Ÿ“"
},
"English": {
"topics": ["Grammar", "Vocabulary", "Comprehension", "Writing"],
"icon": "๐Ÿ“š"
}
}
self.difficulty_levels = {
1: "Beginner", 2: "Easy", 3: "Medium", 4: "Hard", 5: "Expert"
}
self.student_id = "student_001"
def generate_question(self, subject, topic, difficulty):
"""Generate a new question"""
if subject == "Mathematics":
question, answer = self.ai.generate_math_question(topic, difficulty)
else:
question = f"Practice {topic} in {subject}. Write your answer below."
answer = "Sample answer for practice."
# Store in DB
conn = get_db_connection("questions")
cur = conn.cursor()
cur.execute('''
INSERT INTO questions (subject, topic, question_text, difficulty, correct_answer)
VALUES (?, ?, ?, ?, ?)
''', (subject, topic, question, difficulty, answer))
question_id = cur.lastrowid
conn.commit()
return {
"id": question_id,
"text": question,
"subject": subject,
"topic": topic,
"difficulty": self.difficulty_levels.get(difficulty, "Medium")
}
def submit_answer(self, question_id, student_answer):
"""Submit and evaluate answer"""
# Get question
conn = get_db_connection("questions")
cur = conn.cursor()
cur.execute('SELECT correct_answer FROM questions WHERE id = ?', (question_id,))
row = cur.fetchone()
if not row:
return {"feedback": "Question not found.", "score": 0}
correct_answer = row[0]
evaluation = self.ai.evaluate_answer(student_answer, correct_answer)
# Record progress
conn = get_db_connection("students")
cur = conn.cursor()
cur.execute('''
INSERT INTO student_progress (student_id, question_id, student_answer, correct, score)
VALUES (?, ?, ?, ?, ?)
''', (self.student_id, question_id, student_answer, evaluation["correct"], evaluation["score"]))
# Update stats
cur.execute('''
INSERT OR REPLACE INTO student_stats (student_id, total_questions, correct_answers, total_score)
VALUES (?,
COALESCE((SELECT total_questions FROM student_stats WHERE student_id = ?), 0) + 1,
COALESCE((SELECT correct_answers FROM student_stats WHERE student_id = ?), 0) + ?,
COALESCE((SELECT total_score FROM student_stats WHERE student_id = ?), 0) + ?
)
''', (self.student_id, self.student_id, self.student_id,
1 if evaluation["correct"] else 0, self.student_id, evaluation["score"]))
conn.commit()
return evaluation
def get_stats(self):
"""Get student statistics"""
conn = get_db_connection("students")
cur = conn.cursor()
cur.execute('''
SELECT total_questions, correct_answers, total_score, level
FROM student_stats WHERE student_id = ?
''', (self.student_id,))
row = cur.fetchone()
if not row:
return {"total_questions": 0, "correct_answers": 0, "accuracy": 0, "level": 1}
total, correct, score, level = row
accuracy = (correct / total * 100) if total > 0 else 0
return {
"total_questions": total,
"correct_answers": correct,
"accuracy": round(accuracy, 1),
"total_score": round(score, 1),
"level": level
}
# Initialize
tutor = SEAITutor()
# ==================== GRADIO UI ====================
def create_ui():
with gr.Blocks(
title="SEA Prep Pro - Free AI Tutor ๐Ÿ‡น๐Ÿ‡น",
theme=gr.themes.Soft(primary_hue="blue", secondary_hue="purple")
) as demo:
# Header
gr.Markdown("""
# ๐Ÿค– SEA Prep Pro - Free AI Tutor ๐Ÿ‡น๐Ÿ‡น
### 100% Free โ€ข No API Keys โ€ข Trinidad & Tobago SEA Exam Preparation
""")
with gr.Tabs():
with gr.TabItem("๐ŸŽฏ Practice"):
with gr.Row():
with gr.Column(scale=1):
subject = gr.Dropdown(
choices=["Mathematics", "English"],
value="Mathematics",
label="Subject"
)
topic = gr.Dropdown(
choices=tutor.subjects["Mathematics"]["topics"],
value="Fractions",
label="Topic"
)
difficulty = gr.Slider(
1, 5, value=3, step=1,
label="Difficulty Level"
)
generate_btn = gr.Button("โœจ Generate Question", variant="primary")
gr.Markdown("---")
stats = gr.JSON(label="๐Ÿ“Š Your Stats")
refresh_btn = gr.Button("๐Ÿ”„ Refresh Stats")
with gr.Column(scale=2):
question_display = gr.Markdown("### Your question will appear here")
answer_input = gr.Textbox(label="Your Answer", lines=3)
with gr.Row():
submit_btn = gr.Button("โœ… Submit Answer", variant="primary")
clear_btn = gr.Button("๐Ÿ—‘๏ธ Clear")
feedback = gr.Markdown("### ๐Ÿ’ฌ Feedback")
with gr.TabItem("๐Ÿ“ˆ Progress"):
gr.Markdown("### Your Learning Journey")
progress_stats = gr.JSON()
gr.Markdown("### ๐ŸŽฏ Tips for Success")
gr.Markdown("""
- Practice regularly
- Review your mistakes
- Ask for help when needed
- Stay consistent
""")
with gr.TabItem("โ„น๏ธ About"):
gr.Markdown("""
## About SEA Prep Pro
**SEA Prep Pro** is a completely free AI-powered tutoring system for Trinidad and Tobago students.
### Features:
- โœ… 100% Free - No payments ever
- ๐Ÿ”’ No API Keys Required
- ๐Ÿค– Smart Question Generation
- ๐Ÿ“Š Progress Tracking
- ๐Ÿ‡น๐Ÿ‡น Trinidad & Tobago Focus
### Subjects:
- **Mathematics**: Fractions, Geometry, Algebra
- **English**: Grammar, Vocabulary, Comprehension
### How to Use:
1. Select a subject and topic
2. Choose difficulty level
3. Generate questions
4. Submit answers
5. Track your progress
**Made with โค๏ธ for Trinidad and Tobago students**
""")
# State
current_q = gr.State()
# Functions
def update_topics(subject):
topics = tutor.subjects.get(subject, {}).get("topics", [])
return gr.Dropdown.update(choices=topics, value=topics[0] if topics else "")
def generate(subject, topic, difficulty):
q = tutor.generate_question(subject, topic, difficulty)
display = f"""
**Subject:** {q['subject']}
**Topic:** {q['topic']}
**Difficulty:** {q['difficulty']}
---
**Question:**
{q['text']}
"""
return display, q, ""
def submit(answer, question):
if not question:
return "Please generate a question first!", question
result = tutor.submit_answer(question["id"], answer)
return f"**{result['feedback']}** \n**Score:** {result['score']:.1%}", question
def get_stats_func():
return tutor.get_stats()
def clear():
return "", ""
# Event handlers
subject.change(update_topics, subject, topic)
generate_btn.click(generate, [subject, topic, difficulty], [question_display, current_q, feedback])
submit_btn.click(submit, [answer_input, current_q], [feedback, current_q])
refresh_btn.click(get_stats_func, outputs=stats)
clear_btn.click(clear, outputs=[answer_input, feedback])
# Initialize
demo.load(get_stats_func, outputs=stats)
return demo
# Launch
if __name__ == "__main__":
app = create_ui()
app.launch(debug=True)