import gradio as gr
import sqlite3
import json
import os
from datetime import datetime
import torch
import nltk
from transformers import (
T5Tokenizer,
T5ForConditionalGeneration,
ElectraTokenizer,
ElectraForTokenClassification
)
import torch.nn as nn
from tqdm import tqdm
# Download NLTK data
try:
nltk.data.find('tokenizers/punkt')
except LookupError:
nltk.download('punkt')
class HuggingFaceT5GEDInference:
def __init__(self, model_name="Zlovoblachko/REAlEC_2step_model_testing",
ged_model_name="Zlovoblachko/11tag-electra-grammar-stage2", device=None):
"""
Initialize the inference class for T5-GED model from HuggingFace
Args:
model_name: HuggingFace model name/path for the T5-GED model
ged_model_name: HuggingFace model name/path for the GED model
device: Device to run inference on (cuda/cpu)
"""
self.device = device if device else torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Load GED model and tokenizer (same as training)
print(f"Loading GED model from HuggingFace: {ged_model_name}...")
self.ged_model, self.ged_tokenizer = self._load_ged_model(ged_model_name)
# Load T5 model and tokenizer from HuggingFace
print(f"Loading T5 model from HuggingFace: {model_name}...")
self.t5_tokenizer = T5Tokenizer.from_pretrained(model_name)
self.t5_model = T5ForConditionalGeneration.from_pretrained(model_name)
self.t5_model.to(self.device)
# Create GED encoder (copy of T5 encoder)
self.ged_encoder = T5ForConditionalGeneration.from_pretrained(model_name).encoder
self.ged_encoder.to(self.device)
# Create gating mechanism
encoder_hidden_size = self.t5_model.config.d_model
self.gate = nn.Linear(2 * encoder_hidden_size, 1)
self.gate.to(self.device)
# Try to load GED components from HuggingFace
try:
print("Loading GED components...")
from huggingface_hub import hf_hub_download
ged_components_path = hf_hub_download(
repo_id=model_name,
filename="ged_components.pt",
cache_dir=None
)
ged_components = torch.load(ged_components_path, map_location=self.device)
self.ged_encoder.load_state_dict(ged_components["ged_encoder"])
self.gate.load_state_dict(ged_components["gate"])
print("GED components loaded successfully!")
except Exception as e:
print(f"Warning: Could not load GED components: {e}")
print("Using default initialization for GED encoder and gate.")
# Set to evaluation mode
self.t5_model.eval()
self.ged_encoder.eval()
self.gate.eval()
def _load_ged_model(self, model_name):
"""Load GED model and tokenizer from HuggingFace"""
tokenizer = ElectraTokenizer.from_pretrained(model_name)
model = ElectraForTokenClassification.from_pretrained(model_name)
model.to(self.device)
model.eval()
return model, tokenizer
def _get_ged_predictions(self, text):
"""Get GED predictions for input text - exact same as training preprocessing"""
inputs = self.ged_tokenizer(text, return_tensors="pt", truncation=True, padding=True).to(self.device)
with torch.no_grad():
outputs = self.ged_model(**inputs)
logits = outputs.logits
predictions = torch.argmax(logits, dim=2)
token_predictions = predictions[0].cpu().numpy().tolist()
tokens = self.ged_tokenizer.convert_ids_to_tokens(inputs.input_ids[0])
ged_tags = []
for token, pred in zip(tokens, token_predictions):
if token.startswith("##") or token in ["[CLS]", "[SEP]", "[PAD]"]:
continue
ged_tags.append(str(pred))
return " ".join(ged_tags), tokens, token_predictions
def _get_error_spans(self, text):
"""Extract error spans with simplified categories for display"""
ged_tags_str, tokens, predictions = self._get_ged_predictions(text)
error_spans = []
clean_tokens = []
for token, pred in zip(tokens, predictions):
if token.startswith("##") or token in ["[CLS]", "[SEP]", "[PAD]"]:
continue
clean_tokens.append(token)
if pred != 0: # 0 is correct, others are various error types
# Simplify the 11-tag system to basic categories for user display
if pred in [1, 2, 3, 4]: # Various replacement/substitution errors
error_type = "Grammar"
elif pred in [5, 6]: # Missing elements
error_type = "Missing"
elif pred in [7, 8]: # Unnecessary elements
error_type = "Unnecessary"
elif pred in [9, 10]: # Other error types
error_type = "Usage"
else:
error_type = "Error"
error_spans.append({
"token": token,
"type": error_type,
"position": len(clean_tokens) - 1
})
return error_spans
def _get_error_spans_detailed(self, text):
"""Extract error spans with detailed second_level_tag categories"""
ged_tags_str, tokens, predictions = self._get_ged_predictions(text)
error_spans = []
error_types = []
clean_tokens = []
# Correct id2label mapping
id2label = {
0: "correct",
1: "ORTH",
2: "FORM",
3: "MORPH",
4: "DET",
5: "POS",
6: "VERB",
7: "NUM",
8: "WORD",
9: "PUNCT",
10: "RED",
11: "MULTIWORD",
12: "SPELL"
}
for token, pred in zip(tokens, predictions):
if token.startswith("##") or token in ["[CLS]", "[SEP]", "[PAD]"]:
continue
clean_tokens.append(token)
if pred != 0: # 0 is correct, others are various error types
error_type = id2label.get(pred, "OTHER")
error_types.append(error_type)
error_spans.append({
"token": token,
"type": error_type,
"position": len(clean_tokens) - 1
})
return error_spans, list(set(error_types))
def _preprocess_inputs(self, text, max_length=128):
"""Preprocess input text exactly as during training"""
# Get GED predictions
ged_tags, _, _ = self._get_ged_predictions(text)
# Tokenize source text (same as training)
src_tokens = self.t5_tokenizer(
text,
truncation=True,
max_length=max_length,
return_tensors="pt"
)
# Tokenize GED tags (same as training)
ged_tokens = self.t5_tokenizer(
ged_tags,
truncation=True,
max_length=max_length,
return_tensors="pt"
)
return {
"input_ids": src_tokens.input_ids.to(self.device),
"attention_mask": src_tokens.attention_mask.to(self.device),
"ged_input_ids": ged_tokens.input_ids.to(self.device),
"ged_attention_mask": ged_tokens.attention_mask.to(self.device)
}
def _forward_with_ged(self, input_ids, attention_mask, ged_input_ids, ged_attention_mask, max_length=200):
"""
Forward pass with GED integration - replicates T5WithGED.forward() logic
"""
# Get source encoder outputs
src_encoder_outputs = self.t5_model.encoder(
input_ids=input_ids,
attention_mask=attention_mask,
return_dict=True
)
# Get GED encoder outputs
ged_encoder_outputs = self.ged_encoder(
input_ids=ged_input_ids,
attention_mask=ged_attention_mask,
return_dict=True
)
# Get hidden states
src_hidden_states = src_encoder_outputs.last_hidden_state
ged_hidden_states = ged_encoder_outputs.last_hidden_state
# Combine hidden states (same as training)
min_len = min(src_hidden_states.size(1), ged_hidden_states.size(1))
combined = torch.cat([
src_hidden_states[:, :min_len, :],
ged_hidden_states[:, :min_len, :]
], dim=2)
# Apply gating mechanism
gate_scores = torch.sigmoid(self.gate(combined))
combined_hidden = (
gate_scores * src_hidden_states[:, :min_len, :] +
(1 - gate_scores) * ged_hidden_states[:, :min_len, :]
)
# Update encoder outputs
src_encoder_outputs.last_hidden_state = combined_hidden
# Generate using T5 decoder
decoder_outputs = self.t5_model.generate(
encoder_outputs=src_encoder_outputs,
max_length=max_length,
do_sample=False,
num_beams=1
)
return decoder_outputs
def correct_text(self, text, max_length=200):
"""
Correct grammatical errors in input text
Args:
text: Input text to correct
max_length: Maximum length for generation
Returns:
Corrected text as string
"""
# Preprocess inputs exactly as training
inputs = self._preprocess_inputs(text)
# Generate correction using GED-enhanced model
with torch.no_grad():
generated_ids = self._forward_with_ged(
input_ids=inputs["input_ids"],
attention_mask=inputs["attention_mask"],
ged_input_ids=inputs["ged_input_ids"],
ged_attention_mask=inputs["ged_attention_mask"],
max_length=max_length
)
# Decode output
corrected_text = self.t5_tokenizer.decode(generated_ids[0], skip_special_tokens=True)
return corrected_text
def analyze_text(self, text):
"""Enhanced analysis method for Gradio integration"""
if not text.strip():
return "Model not available or empty text", ""
try:
# Get corrected text
corrected_text = self.correct_text(text)
# Get error spans (use the original method for display)
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 generate_html_analysis(self, original, corrected, error_spans):
"""Generate enhanced HTML analysis output"""
# Create highlighted original text
highlighted_original = original
if error_spans:
# Sort by position in reverse to avoid index shifting
sorted_spans = sorted(error_spans, key=lambda x: x['position'], reverse=True)
# Simple highlighting - in a more sophisticated version, you'd map token positions to character positions
for span in sorted_spans:
token = span['token']
error_type = span['type']
# Color coding for different error types
color_map = {
"Grammar": "#ffebee", # Light red
"Missing": "#e8f5e8", # Light green
"Unnecessary": "#fff3e0", # Light orange
"Usage": "#e3f2fd" # Light blue
}
color = color_map.get(error_type, "#f5f5f5")
# Simple token replacement (basic highlighting)
if token in highlighted_original:
highlighted_original = highlighted_original.replace(
token,
f"{token}",
1
)
html = f"""
Grammar Analysis Results
Original Text with Error Highlighting:
{highlighted_original}
Corrected Text:
{corrected}
Error Summary:
Found {len(error_spans)} potential issues
Grammar
Missing
Unnecessary
Usage
"""
return html
def clear_and_reload_database():
"""Clear and reload the sentence database"""
conn = sqlite3.connect('language_app.db')
c = conn.cursor()
# Clear existing data
c.execute("DELETE FROM sentence_database")
conn.commit()
print("Cleared existing sentence database")
conn.close()
# Reload
load_sentence_database()
# 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
)''')
# Sentence database table - ADD THIS
c.execute('''CREATE TABLE IF NOT EXISTS sentence_database (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT NOT NULL,
tags TEXT NOT NULL,
error_types TEXT NOT NULL
)''')
conn.commit()
conn.close()
def load_sentence_database(jsonl_file_path='sentencewise_full.jsonl'):
"""Load sentence database from JSONL file"""
print(f"Debug: Attempting to load from: {jsonl_file_path}")
print(f"Debug: Current working directory: {os.getcwd()}")
print(f"Debug: File exists: {os.path.exists(jsonl_file_path)}")
conn = sqlite3.connect('language_app.db')
c = conn.cursor()
# Create sentence database table
c.execute('''CREATE TABLE IF NOT EXISTS sentence_database (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT NOT NULL,
tags TEXT NOT NULL,
error_types TEXT NOT NULL
)''')
# Check if data already loaded
c.execute("SELECT COUNT(*) FROM sentence_database")
current_count = c.fetchone()[0]
if current_count > 0:
print(f"Sentence database already loaded with {current_count} sentences")
conn.close()
return
# Load JSONL file
try:
print(f"Debug: Opening file {jsonl_file_path}")
with open(jsonl_file_path, 'r', encoding='utf-8') as f:
lines_processed = 0
for line_num, line in enumerate(f, 1):
try:
line = line.strip()
if not line: # Skip empty lines
continue
data = json.loads(line)
text = data.get('text', '')
tags = data.get('tags', [])
if not text or not tags:
print(f"Debug: Skipping line {line_num} - missing text or tags")
continue
# Extract second_level_tag error types
error_types = []
for tag in tags:
second_level = tag.get('second_level_tag', '')
if second_level:
error_types.append(second_level)
error_types = list(set(error_types)) # Remove duplicates
# Debug: Print first few entries
if line_num <= 3:
print(f"Debug line {line_num}: text='{text[:50]}...', error_types={error_types}")
print(f"Debug: Raw tags for line {line_num}: {tags}")
if error_types: # Only insert if we have error types
c.execute("""INSERT INTO sentence_database (text, tags, error_types)
VALUES (?, ?, ?)""",
(text, json.dumps(tags), json.dumps(error_types)))
lines_processed += 1
if line_num % 1000 == 0:
print(f"Processed {line_num} lines, inserted {lines_processed} sentences...")
except json.JSONDecodeError as e:
print(f"JSON decode error on line {line_num}: {e}")
print(f"Line content: {line[:100]}...")
continue
except Exception as e:
print(f"Error processing line {line_num}: {e}")
continue
conn.commit()
print(f"Successfully loaded sentence database with {lines_processed} sentences from {line_num} total lines")
except FileNotFoundError:
print(f"Error: {jsonl_file_path} not found in {os.getcwd()}")
print("Available files:")
try:
files = os.listdir('.')
for f in files:
if f.endswith('.jsonl') or f.endswith('.json'):
print(f" - {f}")
except:
print(" Could not list files")
except Exception as e:
print(f"Error loading sentence database: {e}")
conn.close()
def find_similar_sentences(error_types, limit=5):
"""Find sentences with similar error types from database"""
if not error_types:
return []
conn = sqlite3.connect('language_app.db')
c = conn.cursor()
# Build query to find sentences with matching error types
similar_sentences = []
for error_type in error_types:
c.execute("""SELECT text, tags FROM sentence_database
WHERE error_types LIKE ?
ORDER BY RANDOM()
LIMIT ?""", (f'%"{error_type}"%', limit))
results = c.fetchall()
for text, tags_json in results:
similar_sentences.append({
'text': text,
'tags': json.loads(tags_json)
})
conn.close()
# Remove duplicates and limit to requested number
seen_texts = set()
unique_sentences = []
for sentence in similar_sentences:
if sentence['text'] not in seen_texts:
seen_texts.add(sentence['text'])
unique_sentences.append(sentence)
if len(unique_sentences) >= limit:
break
return unique_sentences
# Initialize database and components
init_database()
print("Clearing and loading sentence database...")
clear_and_reload_database()
print("Initializing enhanced grammar checker...")
grammar_checker = HuggingFaceT5GEDInference()
print("Grammar checker initialized successfully!")
# 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 with enhanced model
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 using sentence database"""
if not text.strip():
return "Please enter text to create an exercise.", ""
# Analyze text to extract error types
sentences = nltk.sent_tokenize(text)
exercise_sentences = []
all_error_types = []
for sentence in sentences:
# Get detailed error analysis
error_spans, error_types = grammar_checker._get_error_spans_detailed(sentence)
if error_types: # Has errors
corrected, _ = grammar_checker.analyze_text(sentence)
exercise_sentences.append({
"original": sentence.strip(),
"corrected": corrected.strip(),
"error_types": error_types
})
all_error_types.extend(error_types)
if not exercise_sentences:
return "No errors found in the text. Cannot create exercise.", ""
# Find similar sentences from database
unique_error_types = list(set(all_error_types))
similar_sentences = find_similar_sentences(unique_error_types, limit=5)
# Combine original sentences with similar ones from database
all_exercise_sentences = exercise_sentences.copy()
for similar in similar_sentences:
# Get corrected version of similar sentence
corrected, _ = grammar_checker.analyze_text(similar['text'])
all_exercise_sentences.append({
"original": similar['text'],
"corrected": corrected,
"error_types": [tag.get('second_level_tag', '') for tag in similar['tags']]
})
# 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(all_exercise_sentences)))
exercise_id = c.lastrowid
conn.commit()
conn.close()
# Generate exercise HTML
exercise_html = f"""
{exercise_title}
Exercise ID: {exercise_id}
Instructions: Correct the grammatical errors in the following sentences:
Error types found: {', '.join(unique_error_types)}
"""
for i, sentence_data in enumerate(all_exercise_sentences, 1):
error_info = f" (Error types: {', '.join(sentence_data.get('error_types', []))})" if sentence_data.get('error_types') else ""
exercise_html += f"- {sentence_data['original']}{error_info}
"
exercise_html += "
"
return f"Exercise created with {len(all_exercise_sentences)} sentences ({len(exercise_sentences)} original + {len(similar_sentences)} from database)! Exercise ID: {exercise_id}", exercise_html
def attempt_exercise(exercise_id, student_responses, student_name):
"""Submit exercise attempt and get score using enhanced analysis"""
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 using enhanced analysis
correct_count = 0
detailed_results = []
for i, (sentence_data, response) in enumerate(zip(exercise_sentences, responses), 1):
original = sentence_data['original']
expected = sentence_data['corrected']
# Use the model to check if the response is correct
response_corrected, response_analysis = grammar_checker.analyze_text(response)
is_correct = response_corrected.strip() == response.strip() # No further corrections needed
if is_correct:
correct_count += 1
detailed_results.append({
'sentence_num': i,
'original': original,
'student_response': response,
'expected': expected,
'model_correction': response_corrected,
'is_correct': is_correct,
'analysis_html': response_analysis
})
score = (correct_count / len(exercise_sentences)) * 100
# Store attempt
attempt_data = {
"responses": responses,
"score": score,
"detailed_results": detailed_results
}
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()
# Create beautiful HTML results
score_color = "#28a745" if score >= 70 else "#ffc107" if score >= 50 else "#dc3545"
feedback_html = f"""
📊 Exercise Results
{score:.1f}%
{correct_count} out of {len(exercise_sentences)} sentences correct
"""
if score >= 90:
feedback_html += """🏆 Excellent Work!"""
elif score >= 70:
feedback_html += """👍 Good Job!"""
elif score >= 50:
feedback_html += """📚 Keep Practicing!"""
else:
feedback_html += """💪 Try Again!"""
feedback_html += """
"""
for result in detailed_results:
# Determine colors and icons
if result['is_correct']:
border_color = "#28a745"
icon = "✅"
status_bg = "#d4edda"
status_text = "Correct!"
else:
border_color = "#dc3545"
icon = "❌"
status_bg = "#f8d7da"
status_text = "Needs Improvement"
feedback_html += f"""
{icon}
Sentence {result['sentence_num']}
{status_text}
📝 Original:
{result['original']}
✏️ Your Answer:
{result['student_response']}
"""
# Only show model analysis if there were errors in student's response
if not result['is_correct'] and result['analysis_html']:
feedback_html += f"""
🔍 Grammar Analysis of Your Response:
{result['analysis_html']}
"""
feedback_html += """
"""
feedback_html += """
💡 Tip: Review the grammar analysis above to understand common error patterns and improve your writing!
"""
return f"Score: {score:.1f}%", feedback_html
def preview_exercise(exercise_id):
"""Preview an exercise before attempting it"""
if not exercise_id.strip():
return "Please enter an exercise ID.", ""
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 title, instructions, sentences FROM exercises WHERE id = ?", (exercise_id,))
result = c.fetchone()
if not result:
return "Exercise not found.", ""
title, instructions, sentences_json = result
exercise_sentences = json.loads(sentences_json)
conn.close()
# Create preview HTML
preview_html = f"""
📋 {title}
Exercise ID: {exercise_id}
📝 Instructions:
{instructions}
💡 Tip: Read each sentence carefully and identify grammatical errors before writing your corrections.
📚 Sentences to Correct ({len(exercise_sentences)} total):
"""
for i, sentence_data in enumerate(exercise_sentences, 1):
original = sentence_data['original']
error_types = sentence_data.get('error_types', [])
# Add error type hints if available
error_hint = ""
if error_types:
error_hint = f"
💡 Focus on: {', '.join(error_types)}"
preview_html += f"""
-
{original}
{error_hint}
"""
preview_html += f"""
🎯 How to Complete This Exercise:
- Read each sentence carefully
- Identify grammatical errors (spelling, grammar, word choice, etc.)
- Write your corrected version of each sentence
- Enter all your answers in the text box below (one sentence per line)
- Submit to get immediate feedback and scoring
"""
return f"Exercise '{title}' loaded successfully! {len(exercise_sentences)} sentences to correct.", preview_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 - Enhanced Grammar Checker", theme=gr.themes.Soft()) as app:
gr.Markdown("# 🎓 Language Learning Application")
gr.Markdown("### AI-Powered Grammar Checking and Exercise Generation")
gr.Markdown("*Now featuring advanced T5-GED neural network with enhanced error detection*")
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")
# Preview section
with gr.Row():
preview_btn = gr.Button("👀 Preview Exercise", variant="secondary")
preview_result = gr.Textbox(label="Preview Status", lines=1)
preview_display = gr.HTML(label="Exercise Preview")
# Separator
gr.Markdown("---")
# Attempt section
gr.Markdown("### 📝 Complete the Exercise")
student_name_exercise = gr.Textbox(label="Your Name", placeholder="Enter your name")
responses_input = gr.Textbox(
label="Your Answers",
lines=8,
placeholder="After previewing the exercise above, enter your corrected sentences here (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")
# Connect the buttons
preview_btn.click(
preview_exercise,
inputs=[exercise_id_input],
outputs=[preview_result, preview_display]
)
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 T5-GED neural networks for enhanced grammar error detection and correction*
""")
if __name__ == "__main__":
app.launch(share=True)