lang_learn_app / app.py
Zlovoblachko's picture
initial commit
66bc07b
raw
history blame
18.8 kB
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"""
<div style='font-family: Arial, sans-serif; line-height: 1.6; padding: 20px; border: 1px solid #ddd; border-radius: 8px; background-color: #f9f9f9;'>
<h3 style='color: #333; margin-top: 0;'>Grammar Analysis Results</h3>
<div style='margin: 15px 0;'>
<h4 style='color: #555;'>Original Text:</h4>
<p style='padding: 10px; background-color: #fff; border: 1px solid #ddd; border-radius: 4px;'>{original}</p>
</div>
<div style='margin: 15px 0;'>
<h4 style='color: #28a745;'>Corrected Text:</h4>
<p style='padding: 10px; background-color: #d4edda; border: 1px solid #c3e6cb; border-radius: 4px;'>{corrected}</p>
</div>
<div style='margin: 15px 0;'>
<h4 style='color: #333;'>Error Analysis:</h4>
<p style='color: #666;'>Found {len(error_spans)} potential errors</p>
</div>
</div>
"""
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"""
<div style='font-family: Arial, sans-serif; padding: 20px; border: 1px solid #ddd; border-radius: 8px;'>
<h3>{exercise_title}</h3>
<p><strong>Instructions:</strong> Correct the grammatical errors in the following sentences:</p>
<ol>
"""
for i, sentence_data in enumerate(exercise_sentences, 1):
exercise_html += f"<li style='margin: 10px 0; padding: 10px; background-color: #f8f9fa; border-radius: 4px;'>{sentence_data['original']}</li>"
exercise_html += "</ol></div>"
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"""
<div style='font-family: Arial, sans-serif; padding: 20px; border: 1px solid #ddd; border-radius: 8px;'>
<h3>Exercise Results</h3>
<p><strong>Score: {score:.1f}%</strong> ({correct_count}/{len(exercise_sentences)} correct)</p>
<div style='margin-top: 15px;'>
{'<br>'.join(feedback)}
</div>
</div>
"""
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"""
<div style='font-family: Arial, sans-serif; padding: 20px;'>
<h3>Progress for {student_name}</h3>
<h4>Writing Submissions ({len(submissions)})</h4>
<ul>
"""
for sub in submissions:
progress_html += f"<li><strong>{sub[3]}</strong> - {sub[2][:16]} - {len(sub[1])} characters</li>"
progress_html += f"""
</ul>
<h4>Exercise Attempts ({len(attempts)})</h4>
<ul>
"""
for att in attempts:
progress_html += f"<li><strong>{att[2]}</strong> - Score: {att[0]:.1f}% - {att[1][:16]}</li>"
progress_html += "</ul></div>"
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()