import gradio as gr
import sqlite3
import json
import os
from datetime import datetime
import torch
import nltk
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer, ElectraTokenizer, ElectraForTokenClassification
import torch.nn as nn
# Download NLTK data
try:
nltk.data.find('tokenizers/punkt')
except LookupError:
nltk.download('punkt')
# Initialize SQLite database for storing submissions and exercises
def init_database():
conn = sqlite3.connect('language_app.db')
c = conn.cursor()
# Users table
c.execute('''CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
email TEXT UNIQUE NOT NULL,
role TEXT NOT NULL,
password_hash TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)''')
# Tasks table
c.execute('''CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT NOT NULL,
image_url TEXT,
creator_id INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)''')
# Submissions table
c.execute('''CREATE TABLE IF NOT EXISTS submissions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id INTEGER,
student_name TEXT NOT NULL,
content TEXT NOT NULL,
analysis_result TEXT,
analysis_html TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)''')
# Exercises table
c.execute('''CREATE TABLE IF NOT EXISTS exercises (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
instructions TEXT NOT NULL,
sentences TEXT NOT NULL,
image_url TEXT,
submission_id INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)''')
# Exercise attempts table
c.execute('''CREATE TABLE IF NOT EXISTS exercise_attempts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
exercise_id INTEGER,
student_name TEXT NOT NULL,
responses TEXT NOT NULL,
score REAL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)''')
conn.commit()
conn.close()
# Neural Network Model (simplified version of your existing model)
class SimpleGrammarChecker:
def __init__(self):
self.model_name = "Zlovoblachko/Realec-2step-ft-realec"
self.ged_model_name = "Zlovoblachko/4tag-electra-grammar-error-detection"
self.load_models()
def load_models(self):
try:
# Load T5 model
self.tokenizer = AutoTokenizer.from_pretrained(self.model_name)
self.model = AutoModelForSeq2SeqLM.from_pretrained(self.model_name)
# Load GED model
self.ged_tokenizer = ElectraTokenizer.from_pretrained(self.ged_model_name)
self.ged_model = ElectraForTokenClassification.from_pretrained(self.ged_model_name)
print("Models loaded successfully!")
except Exception as e:
print(f"Error loading models: {e}")
self.model = None
self.ged_model = None
def analyze_text(self, text):
if not self.model or not text.strip():
return "Model not available or empty text", ""
try:
# Tokenize and generate correction
inputs = self.tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
with torch.no_grad():
outputs = self.model.generate(
input_ids=inputs.input_ids,
attention_mask=inputs.attention_mask,
max_length=512,
num_beams=4,
early_stopping=True
)
corrected_text = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
# Get GED predictions if available
error_spans = []
if self.ged_model:
error_spans = self.get_error_spans(text)
# Generate HTML output
html_output = self.generate_html_analysis(text, corrected_text, error_spans)
return corrected_text, html_output
except Exception as e:
return f"Error during analysis: {str(e)}", ""
def get_error_spans(self, text):
try:
inputs = self.ged_tokenizer(text, return_tensors="pt", truncation=True, padding=True)
with torch.no_grad():
outputs = self.ged_model(**inputs)
predictions = torch.argmax(outputs.logits, dim=2)
tokens = self.ged_tokenizer.convert_ids_to_tokens(inputs.input_ids[0])
token_predictions = predictions[0].cpu().numpy().tolist()
error_spans = []
for i, (token, pred) in enumerate(zip(tokens, token_predictions)):
if token.startswith("##") or token in ["[CLS]", "[SEP]", "[PAD]"]:
continue
if pred != 0: # 0 is correct, 1=R, 2=M, 3=U
error_type = ["C", "R", "M", "U"][pred]
error_spans.append({"token": token, "type": error_type, "position": i})
return error_spans
except:
return []
def generate_html_analysis(self, original, corrected, error_spans):
html = f"""
Grammar Analysis Results
Original Text:
{original}
Corrected Text:
{corrected}
Error Analysis:
Found {len(error_spans)} potential errors
"""
return html
# Initialize components
init_database()
grammar_checker = SimpleGrammarChecker()
# Gradio Interface Functions
def analyze_student_writing(text, student_name, task_title="General Writing Task"):
"""Analyze student writing and store in database"""
if not text.strip():
return "Please enter some text to analyze.", ""
if not student_name.strip():
return "Please enter your name.", ""
# Analyze text
corrected_text, html_analysis = grammar_checker.analyze_text(text)
# Store in database
conn = sqlite3.connect('language_app.db')
c = conn.cursor()
# Insert task if not exists
c.execute("INSERT OR IGNORE INTO tasks (title, description) VALUES (?, ?)",
(task_title, f"Writing task: {task_title}"))
c.execute("SELECT id FROM tasks WHERE title = ?", (task_title,))
task_id = c.fetchone()[0]
# Insert submission
analysis_data = {
"corrected_text": corrected_text,
"original_text": text,
"html_output": html_analysis
}
c.execute("""INSERT INTO submissions (task_id, student_name, content, analysis_result, analysis_html)
VALUES (?, ?, ?, ?, ?)""",
(task_id, student_name, text, json.dumps(analysis_data), html_analysis))
submission_id = c.lastrowid
conn.commit()
conn.close()
return corrected_text, html_analysis
def create_exercise_from_text(text, exercise_title="Grammar Exercise"):
"""Create an exercise from text with errors"""
if not text.strip():
return "Please enter text to create an exercise.", ""
# Analyze text to find sentences with errors
sentences = nltk.sent_tokenize(text)
exercise_sentences = []
for sentence in sentences:
corrected, _ = grammar_checker.analyze_text(sentence)
if sentence.strip() != corrected.strip(): # Has errors
exercise_sentences.append({
"original": sentence.strip(),
"corrected": corrected.strip()
})
if not exercise_sentences:
return "No errors found in the text. Cannot create exercise.", ""
# Store exercise in database
conn = sqlite3.connect('language_app.db')
c = conn.cursor()
c.execute("""INSERT INTO exercises (title, instructions, sentences)
VALUES (?, ?, ?)""",
(exercise_title,
"Correct the grammatical errors in the following sentences:",
json.dumps(exercise_sentences)))
exercise_id = c.lastrowid
conn.commit()
conn.close()
# Generate exercise HTML
exercise_html = f"""
{exercise_title}
Instructions: Correct the grammatical errors in the following sentences:
"""
for i, sentence_data in enumerate(exercise_sentences, 1):
exercise_html += f"- {sentence_data['original']}
"
exercise_html += "
"
return f"Exercise created with {len(exercise_sentences)} sentences!", exercise_html
def attempt_exercise(exercise_id, student_responses, student_name):
"""Submit exercise attempt and get score"""
if not student_name.strip():
return "Please enter your name.", ""
try:
exercise_id = int(exercise_id)
except:
return "Please enter a valid exercise ID.", ""
# Get exercise from database
conn = sqlite3.connect('language_app.db')
c = conn.cursor()
c.execute("SELECT sentences FROM exercises WHERE id = ?", (exercise_id,))
result = c.fetchone()
if not result:
return "Exercise not found.", ""
exercise_sentences = json.loads(result[0])
# Parse student responses
responses = [r.strip() for r in student_responses.split('\n') if r.strip()]
if len(responses) != len(exercise_sentences):
return f"Please provide exactly {len(exercise_sentences)} responses (one per line).", ""
# Calculate score
correct_count = 0
feedback = []
for i, (sentence_data, response) in enumerate(zip(exercise_sentences, responses), 1):
correct_answer = sentence_data['corrected']
is_correct = response.lower().strip() == correct_answer.lower().strip()
if is_correct:
correct_count += 1
feedback.append(f"✅ Sentence {i}: Correct!")
else:
feedback.append(f"❌ Sentence {i}: Your answer: '{response}' | Correct answer: '{correct_answer}'")
score = (correct_count / len(exercise_sentences)) * 100
# Store attempt
attempt_data = {
"responses": responses,
"score": score,
"feedback": feedback
}
c.execute("""INSERT INTO exercise_attempts (exercise_id, student_name, responses, score)
VALUES (?, ?, ?, ?)""",
(exercise_id, student_name, json.dumps(attempt_data), score))
conn.commit()
conn.close()
feedback_html = f"""
Exercise Results
Score: {score:.1f}% ({correct_count}/{len(exercise_sentences)} correct)
{'
'.join(feedback)}
"""
return f"Score: {score:.1f}%", feedback_html
def get_student_progress(student_name):
"""Get student's submission and exercise history"""
if not student_name.strip():
return "Please enter a student name."
conn = sqlite3.connect('language_app.db')
c = conn.cursor()
# Get submissions
c.execute("""SELECT s.id, s.content, s.created_at, t.title
FROM submissions s JOIN tasks t ON s.task_id = t.id
WHERE s.student_name = ? ORDER BY s.created_at DESC""", (student_name,))
submissions = c.fetchall()
# Get exercise attempts
c.execute("""SELECT ea.score, ea.created_at, e.title
FROM exercise_attempts ea JOIN exercises e ON ea.exercise_id = e.id
WHERE ea.student_name = ? ORDER BY ea.created_at DESC""", (student_name,))
attempts = c.fetchall()
conn.close()
progress_html = f"""
Progress for {student_name}
Writing Submissions ({len(submissions)})
"""
for sub in submissions:
progress_html += f"- {sub[3]} - {sub[2][:16]} - {len(sub[1])} characters
"
progress_html += f"""
Exercise Attempts ({len(attempts)})
"""
for att in attempts:
progress_html += f"- {att[2]} - Score: {att[0]:.1f}% - {att[1][:16]}
"
progress_html += "
"
return progress_html
# Create Gradio Interface
with gr.Blocks(title="Language Learning App - Grammar Checker", theme=gr.themes.Soft()) as app:
gr.Markdown("# 🎓 Language Learning Application")
gr.Markdown("### AI-Powered Grammar Checking and Exercise Generation")
with gr.Tabs():
# Student Writing Analysis Tab
with gr.TabItem("📝 Writing Analysis"):
gr.Markdown("## Submit Your Writing for Analysis")
with gr.Row():
with gr.Column():
student_name_input = gr.Textbox(label="Your Name", placeholder="Enter your name")
task_title_input = gr.Textbox(label="Assignment Title", value="General Writing Task")
writing_input = gr.Textbox(
label="Your Writing",
lines=8,
placeholder="Paste your writing here for grammar analysis..."
)
analyze_btn = gr.Button("Analyze Writing", variant="primary")
with gr.Column():
corrected_output = gr.Textbox(label="Corrected Text", lines=6)
analysis_output = gr.HTML(label="Detailed Analysis")
analyze_btn.click(
analyze_student_writing,
inputs=[writing_input, student_name_input, task_title_input],
outputs=[corrected_output, analysis_output]
)
# Exercise Creation Tab
with gr.TabItem("🏋️ Exercise Creation"):
gr.Markdown("## Create Grammar Exercises")
with gr.Row():
with gr.Column():
exercise_title_input = gr.Textbox(label="Exercise Title", value="Grammar Exercise")
exercise_text_input = gr.Textbox(
label="Text with Errors",
lines=6,
placeholder="Enter text containing grammatical errors to create an exercise..."
)
create_exercise_btn = gr.Button("Create Exercise", variant="primary")
with gr.Column():
exercise_result = gr.Textbox(label="Result")
exercise_display = gr.HTML(label="Generated Exercise")
create_exercise_btn.click(
create_exercise_from_text,
inputs=[exercise_text_input, exercise_title_input],
outputs=[exercise_result, exercise_display]
)
# Exercise Attempt Tab
with gr.TabItem("✏️ Exercise Practice"):
gr.Markdown("## Practice Grammar Exercises")
with gr.Row():
with gr.Column():
exercise_id_input = gr.Textbox(label="Exercise ID", placeholder="Enter exercise ID")
student_name_exercise = gr.Textbox(label="Your Name", placeholder="Enter your name")
responses_input = gr.Textbox(
label="Your Answers",
lines=6,
placeholder="Enter your corrected sentences (one per line)..."
)
submit_exercise_btn = gr.Button("Submit Answers", variant="primary")
with gr.Column():
score_output = gr.Textbox(label="Your Score")
feedback_output = gr.HTML(label="Detailed Feedback")
submit_exercise_btn.click(
attempt_exercise,
inputs=[exercise_id_input, responses_input, student_name_exercise],
outputs=[score_output, feedback_output]
)
# Progress Tracking Tab
with gr.TabItem("📊 Student Progress"):
gr.Markdown("## View Student Progress")
with gr.Row():
with gr.Column(scale=1):
progress_student_name = gr.Textbox(label="Student Name", placeholder="Enter student name")
get_progress_btn = gr.Button("Get Progress", variant="primary")
with gr.Column(scale=2):
progress_output = gr.HTML(label="Student Progress")
get_progress_btn.click(
get_student_progress,
inputs=[progress_student_name],
outputs=[progress_output]
)
gr.Markdown("""
---
### How to Use:
1. **Writing Analysis**: Submit your writing to get grammar corrections and detailed error analysis
2. **Exercise Creation**: Teachers can create exercises from text containing errors
3. **Exercise Practice**: Students can practice with generated exercises and get scored feedback
4. **Progress Tracking**: View student progress across submissions and exercises
*Powered by advanced neural networks for grammar error detection and correction*
""")
if __name__ == "__main__":
app.launch()