easyreadDemo / app.py
kayarn's picture
create app.py
cb2ca32 verified
"""
GenAI for Easy Read: Open-Source Multimodal Solution
A Gradio-based prototype for converting documents into accessible Easy Read formats.
"""
import gradio as gr
import os
from typing import List, Tuple, Optional
import re
# ============================================================================
# DUMMY/MOCK FUNCTIONS FOR BACKEND PROCESSING
# TODO: Replace these with actual API calls to OpenAI, GlobalSymbols, etc.
# ============================================================================
def split_into_sentences(text: str) -> List[str]:
"""
Splits text into individual sentences.
TODO: Enhance with better sentence boundary detection (e.g., using spaCy)
"""
if not text or not text.strip():
return []
# Simple sentence splitting (can be improved with NLP libraries like spaCy)
sentences = re.split(r'(?<=[.!?])\s+', text.strip())
return [s.strip() for s in sentences if s.strip()]
def convert_to_easy_read(
pdf_file: Optional[str],
text_input: Optional[str],
context: str,
unalterable_terms: str
) -> Tuple[str, List[str], List[str]]:
"""
Simulates the conversion of complex text to simplified Easy Read format.
TODO: Replace with actual LLM API call (e.g., OpenAI GPT-4, Anthropic Claude)
- Process PDF if provided, or use text_input
- Apply simplification rules
- Respect context/custom instructions
- Preserve unalterable terms
"""
# Use text_input if provided, otherwise dummy text
input_text = text_input if text_input else (
"This is sentence one. "
"This is sentence two. "
"This is sentence three. "
"Each sentence can be edited separately."
)
# Split into sentences
sentences = split_into_sentences(input_text)
# Dummy simplified text (sentence-by-sentence)
dummy_simplified = "\n".join(sentences)
# Dummy keyword list for symbol matching
dummy_keywords = ["person", "help", "document", "read"]
return dummy_simplified, dummy_keywords, sentences
def update_sentence(sentence_index: int, new_text: str, all_sentences: List[str]) -> str:
"""
Updates a specific sentence and returns the combined text.
"""
if 0 <= sentence_index < len(all_sentences):
all_sentences[sentence_index] = new_text
return "\n".join(all_sentences)
def fetch_symbols_from_libraries(
keywords: List[str], selected_libraries: List[str]
) -> List[str]:
"""
Fetches symbols from selected symbol libraries based on keywords.
TODO: Replace with actual API calls to:
- GlobalSymbols API
- ARASAAC API
- AAC Image Library API
- PiCom API
"""
# Dummy: Return placeholder image paths
# In production, these would be URLs or file paths to actual symbols
dummy_symbols = [
"https://via.placeholder.com/200x200/4A90E2/FFFFFF?text=Symbol1",
"https://via.placeholder.com/200x200/50C878/FFFFFF?text=Symbol2",
"https://via.placeholder.com/200x200/E94B3C/FFFFFF?text=Symbol3",
"https://via.placeholder.com/200x200/F5A623/FFFFFF?text=Symbol4",
]
return dummy_symbols
def generate_ai_image(prompt: str) -> Optional[str]:
"""
Generates an image using AI based on the provided prompt.
TODO: Replace with actual AI image generation API call:
- OpenAI DALL-E
- Stability AI Stable Diffusion
- Midjourney API
- Other image generation services
"""
# Dummy: Return placeholder image
return "https://via.placeholder.com/400x400/9B59B6/FFFFFF?text=AI+Generated"
def export_to_pdf(text: str, images: List[str]) -> str:
"""
Exports the Easy Read document to PDF format.
TODO: Implement PDF generation using libraries like:
- reportlab
- fpdf
- weasyprint
"""
return "PDF export functionality - TODO: Implement"
def export_to_word(text: str, images: List[str]) -> str:
"""
Exports the Easy Read document to Word format.
TODO: Implement Word document generation using:
- python-docx
"""
return "Word export functionality - TODO: Implement"
def generate_audio(text: str) -> str:
"""
Generates audio narration of the Easy Read text.
TODO: Implement text-to-speech using:
- OpenAI TTS API
- Google Cloud Text-to-Speech
- Amazon Polly
- Azure Cognitive Services
"""
return "Audio generation functionality - TODO: Implement"
# ============================================================================
# GRADIO INTERFACE COMPONENTS
# ============================================================================
def create_convert_handler(
pdf_file, text_input, context, unalterable_terms, selected_libraries
):
"""
Main handler for the "Convert to Easy Read" button.
Processes input and returns simplified text and symbol suggestions.
"""
# Step 1: Convert text to Easy Read format
simplified_text, keywords, sentences = convert_to_easy_read(
pdf_file, text_input, context, unalterable_terms
)
# Step 2: Fetch symbols from selected libraries
symbols = fetch_symbols_from_libraries(keywords, selected_libraries)
# Step 3: Create sentence components for display
sentence_components = []
for i, sentence in enumerate(sentences):
sentence_components.append((sentence, i))
return simplified_text, symbols, sentences
def create_ai_image_handler(prompt: str):
"""Handler for AI image generation tab."""
if not prompt.strip():
return None
return generate_ai_image(prompt)
# ============================================================================
# MAIN GRADIO INTERFACE
# ============================================================================
def create_interface():
"""Creates and configures the main Gradio interface."""
with gr.Blocks(theme=gr.themes.Soft()) as app:
# Store sentences in state
sentences_state = gr.State([])
# ====================================================================
# HEADER SECTION
# ====================================================================
gr.Markdown(
"""
# GenAI for Easy Read: Open-Source Multimodal Solution
Convert documents into accessible Easy Read formats with simplified text and supported symbols.
""",
elem_classes=["header"]
)
# ====================================================================
# STEP 1: INPUT SECTION
# ====================================================================
gr.Markdown("## Step 1: Input Document or Text")
with gr.Row():
# Left Column: File Upload
with gr.Column():
pdf_upload = gr.File(
label="Upload Document",
file_types=[".pdf"],
type="filepath"
)
# Right Column: Text Input
with gr.Column():
text_input = gr.Textbox(
label="Or Paste Text Here",
lines=10,
placeholder="Enter your text here..."
)
# Advanced Settings Accordion
with gr.Accordion("Advanced Settings", open=False):
context_input = gr.Textbox(
label="Context/Custom Instructions",
placeholder="e.g., 'never use term X', 'target audience: children'",
lines=3
)
unalterable_terms_input = gr.Textbox(
label="Unalterable Terms",
placeholder="Enter terms that must not be changed (comma-separated)",
lines=2
)
# ====================================================================
# STEP 2: SYMBOL CONFIGURATION
# ====================================================================
gr.Markdown("## Step 2: Select Symbol Libraries")
symbol_libraries = gr.CheckboxGroup(
choices=[
"AAC Image Library",
"GlobalSymbols",
"ARASAAC",
"PiCom",
"AI Realistic Symbols"
],
label="Available Symbol Libraries",
value=["ARASAAC", "GlobalSymbols"] # Default selections
)
# ====================================================================
# STEP 3: EASY READ EDITOR
# ====================================================================
gr.Markdown("## Step 3: Easy Read Editor")
# Convert Button
convert_btn = gr.Button(
"Convert to Easy Read",
variant="primary",
size="lg"
)
# Main Editor Layout: Text Editing + Image Selection
with gr.Row():
# Left Side: Full Text Editor
with gr.Column(scale=1):
simplified_text = gr.Textbox(
label="Full Simplified Text (Editable)",
lines=15,
placeholder="Simplified text will appear here after conversion..."
)
# Right Side: Multi-Tab Image Interface
with gr.Column(scale=1):
with gr.Tabs() as image_tabs:
# Tab 1: Symbol Library / Alternatives
with gr.Tab("Symbol Library / Alternatives"):
symbol_gallery = gr.Gallery(
label="Available Symbols",
show_label=True,
elem_id="symbol_gallery",
columns=3,
rows=2,
height="auto"
)
# Tab 2: Generate with AI
with gr.Tab("Generate with AI"):
ai_prompt = gr.Textbox(
label="Image Prompt",
placeholder="Describe the image you want to generate...",
lines=3
)
generate_ai_btn = gr.Button("Generate", variant="secondary")
ai_generated_image = gr.Image(
label="Generated Image",
type="filepath"
)
# Tab 3: Upload / Personal
with gr.Tab("Upload / Personal"):
personal_image_upload = gr.Image(
label="Upload Your Image",
sources=["upload"],
type="filepath"
)
# Tab 4: Favorites
with gr.Tab("Favorites"):
favorites_gallery = gr.Gallery(
label="Saved Favorites",
show_label=True,
elem_id="favorites_gallery",
columns=3,
rows=2,
height="auto"
)
# ====================================================================
# INDIVIDUAL SENTENCE EDITOR
# ====================================================================
gr.Markdown("## Individual Sentence Editor")
gr.Markdown("*Edit each sentence separately below*")
# Container for individual sentences
sentence_editor_container = gr.Column()
with sentence_editor_container:
# Dynamic sentence textboxes will be created here
sentence_textboxes = []
for i in range(10): # Pre-create 10 textboxes (will show/hide as needed)
with gr.Row(visible=False) as sentence_row:
sentence_num = gr.Markdown(f"**Sentence {i+1}:**")
sentence_box = gr.Textbox(
label="",
lines=2,
show_label=False,
scale=4
)
sentence_textboxes.append((sentence_row, sentence_box, sentence_num))
# ====================================================================
# FOOTER / EXPORT SECTION
# ====================================================================
gr.Markdown("## Export Options")
with gr.Row():
export_pdf_btn = gr.Button("Export to PDF", variant="secondary")
export_word_btn = gr.Button("Export to Word", variant="secondary")
generate_audio_btn = gr.Button("Generate Audio", variant="secondary")
# ====================================================================
# EVENT HANDLERS
# ====================================================================
def update_sentence_display(sentences):
"""Updates the visibility and content of sentence textboxes"""
updates = []
for i, (row, box, num) in enumerate(sentence_textboxes):
if i < len(sentences):
# Show this sentence
updates.extend([
gr.update(visible=True), # row
gr.update(value=sentences[i]), # textbox
gr.update(value=f"**Sentence {i+1}:**") # label
])
else:
# Hide this sentence
updates.extend([
gr.update(visible=False), # row
gr.update(value=""), # textbox
gr.update(value="") # label
])
return updates
def merge_sentences_to_full_text(*sentence_values):
"""Merges individual sentence values back into full text"""
# Filter out empty sentences
sentences = [s for s in sentence_values if s and s.strip()]
return "\n".join(sentences)
# Convert button handler
def convert_handler(*args):
simplified_text, symbols, sentences = create_convert_handler(*args)
# Update sentence display
sentence_updates = update_sentence_display(sentences)
return [simplified_text, symbols, sentences] + sentence_updates
# Collect all outputs for convert button
convert_outputs = [simplified_text, symbol_gallery, sentences_state]
for row, box, num in sentence_textboxes:
convert_outputs.extend([row, box, num])
convert_btn.click(
fn=convert_handler,
inputs=[
pdf_upload,
text_input,
context_input,
unalterable_terms_input,
symbol_libraries
],
outputs=convert_outputs
)
# Update full text when any sentence is edited
for row, box, num in sentence_textboxes:
box.change(
fn=merge_sentences_to_full_text,
inputs=[b for r, b, n in sentence_textboxes],
outputs=[simplified_text]
)
# AI image generation handler
generate_ai_btn.click(
fn=create_ai_image_handler,
inputs=[ai_prompt],
outputs=[ai_generated_image]
)
# Export handlers (placeholder)
export_pdf_btn.click(
fn=lambda t, imgs: gr.Info("PDF export - TODO: Implement backend"),
inputs=[simplified_text, symbol_gallery],
outputs=[]
)
export_word_btn.click(
fn=lambda t, imgs: gr.Info("Word export - TODO: Implement backend"),
inputs=[simplified_text, symbol_gallery],
outputs=[]
)
generate_audio_btn.click(
fn=lambda t: gr.Info("Audio generation - TODO: Implement backend"),
inputs=[simplified_text],
outputs=[]
)
return app
def main():
"""Main entry point for the application."""
app = create_interface()
app.launch(
server_name="0.0.0.0",
server_port=7860,
share=False
)
if __name__ == "__main__":
main()