Teacher-LP / app.py
NazishHasan's picture
Update app.py
fce564d verified
Raw
History Blame Contribute Delete
58.5 kB
import gradio as gr
import os
import re
import tempfile
from io import BytesIO
# python-docx for Word document creation
from docx import Document
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.shared import Inches, Pt, RGBColor
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
from docx.enum.section import WD_ORIENT
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.enum.text import MSO_ANCHOR, MSO_AUTO_SIZE
from pptx.dml.color import RGBColor
# Groq for AI generation
from groq import Groq
# PyPDF2 for PDF extraction
import PyPDF2
# --- CONFIGURATION ---
# Ensure your API key is set as an environment variable (e.g., export groq_api_key='YOUR_KEY')
groq_client = Groq(api_key=os.environ.get("groq_api_key"))
# IMPORTANT: Ensure these paths are correct relative to your app.py file
LP_TEMPLATE_PATH = "LP-Template.docx"
SCHOOL_LOGO_PATH = "school_logo.png"
# Gradio Dropdown Choices
GRADES = [str(i) for i in range(1, 13)]
SUBJECTS = [
"ELA", "Mathematics", "Science", "Social Studies", "ICT/Computer Science",
"Arabic", "Biology", "Chemistry", "Physics", "Islamic Studies", "Business Foundation", "Micro Economics", "Macro Economics", "Art", "Health Education", "Moral Education", "Clubs"
]
# --- END CONFIGURATION ---
# --- Helper Functions for Document Creation ---
def _sanitize_filename(text):
"""Sanitizes text to be used in a filename."""
# Remove characters that are not allowed in Windows filenames
# (also generally good for other OSes)
sanitized = re.sub(r'[<>:"/\\|?*]', '', text)
# Replace spaces with underscores or hyphens, or remove leading/trailing spaces
sanitized = sanitized.strip().replace(' ', '_')
return sanitized
def set_table_borders_and_shading(table, shade_color=None):
"""
Splits a table into multiple pages if it exceeds the height of a single page.
Args:
table: The docx.table.Table object to split.
Returns:
None. Modifies the document in place.
"""
tbl_pr = table._element.xpath('w:tblPr')[0]
tbl_borders = OxmlElement('w:tblBorders')
# Define border properties
borders = ['top', 'left', 'bottom', 'right', 'insideH', 'insideV']
for border_name in borders:
border_el = OxmlElement(f'w:{border_name}')
border_el.set(qn('w:val'), 'single')
border_el.set(qn('w:sz'), '4') # Border size in eighths of a point (1/8 pt)
border_el.set(qn('w:space'), '0')
border_el.set(qn('w:color'), 'auto') # Let Word decide color or specify '000000' for black
tbl_borders.append(border_el)
tbl_pr.append(tbl_borders)
# Optional: Add shading to the first row (header row)
if shade_color:
for cell in table.rows[0].cells:
tcPr = cell._tc.get_or_add_tcPr()
shading_elm = OxmlElement('w:shd')
shading_elm.set(qn('w:val'), 'clear')
shading_elm.set(qn('w:color'), 'auto')
shading_elm.set(qn('w:fill'), shade_color)
tcPr.append(shading_elm)
def replace_placeholders_in_paragraph(paragraph, data_map):
"""
Replaces placeholders like ##FIELD## in a paragraph's runs.
This function processes the paragraph's text to find placeholders
and replaces them with corresponding values from data_map.
It attempts to handle placeholders split across runs.
"""
full_text = "".join([run.text for run in paragraph.runs])
original_runs = list(paragraph.runs) # Keep a reference to original runs for their formatting
# Perform replacements on the combined text
modified_text = full_text
for placeholder, value in data_map.items():
# Ensure value is treated as a string for replacement
modified_text = modified_text.replace(placeholder, str(value))
# If text was replaced, clear existing runs and add new ones to apply changes
# Only modify if the text has actually changed to preserve formatting where possible
if modified_text != full_text: # Check if text was actually modified
# Clear existing runs. Note: Clearing all runs and recreating can lose fine-grained formatting.
# A more robust approach might involve iterating and updating existing runs,
# or splitting new text and applying format from nearest original run.
# For simplicity and common use cases, clearing and adding to first run is often sufficient.
for run in original_runs:
run.clear()
if original_runs:
# Re-add content to the first run (or create a new run if paragraph was empty)
original_runs[0].text = modified_text
else:
paragraph.add_run(modified_text)
def replace_placeholders_in_table(table, data_map):
"""
Replaces placeholders in cells of a table.
"""
for row in table.rows:
for cell in row.cells:
for paragraph in cell.paragraphs:
replace_placeholders_in_paragraph(paragraph, data_map)
# --- PDF Extraction and Parsing Functions ---
def extract_text_from_pdf(pdf_path):
"""
Extracts text from a PDF file given its path.
Returns the text of the first page and the text of the entire document separately.
"""
first_page_text = ""
full_text = ""
if not pdf_path:
return "", ""
file_extension = os.path.splitext(pdf_path)[1].lower()
if file_extension != ".pdf":
return "Error: Only .pdf files are supported.", "Error: Only .pdf files are supported."
try:
with open(pdf_path, 'rb') as file:
reader = PyPDF2.PdfReader(file)
if not reader.pages:
return "Error: PDF has no pages.", "Error: PDF has no pages."
# Extract text from the first page
first_page = reader.pages[0]
if first_page.extract_text():
first_page_text = first_page.extract_text()
# Extract text from the entire document
for page in reader.pages:
page_text = page.extract_text()
if page_text:
full_text += page_text + "\n\n" # Add double newline between pages
except Exception as e:
error_message = f"Error during PDF extraction: {e}"
return error_message, error_message
# Normalize whitespace for cleaner output
first_page_text = first_page_text.strip() # Still normalize first page text
# For full_text, replace sequences of whitespace (including newlines) with a single space,
# BUT preserve actual paragraph breaks (two or more newlines).
# This is a common way to prepare text for multi-line regex.
full_text = re.sub(r'\s*\n\s*\n\s*', '\n\n', full_text) # Convert multiple newlines to double newlines
full_text = re.sub(r'[ \t]+', ' ', full_text) # Collapse spaces/tabs on a single line
full_text = re.sub(r' *\n *', '\n', full_text).strip() # Remove spaces around single newlines, then strip overall
if len(full_text) < 20:
return "Error: PDF contains very little text or is image-based.", "Error: PDF contains very little text or is image-based."
return first_page_text, full_text
def search_field(pattern, text, default=""):
match = re.search(pattern, text, re.IGNORECASE | re.DOTALL)
if match:
return match.group(1).strip()
return default
def extract_list_content(header_patterns, text):
"""
Extracts a block of text that typically represents a list (like objectives, standards).
It looks for any of the header patterns and then captures content until another
common section header is encountered or the end of the text.
This version attempts to be more precise in stopping.
"""
# Combine header patterns with word boundaries
combined_header_pattern = '|'.join([rf"\b{re.escape(p)}\b" for p in header_patterns])
# Define a comprehensive list of common section terminators/headers.
# Ensure these are distinct and cover all possible section starts in your PDFs.
# Include variations with/without colons, and common phrasing.
all_section_headers = [
# Existing headers
"subject:", "class/section:", "date:", "term:", "school:", "teacher’s name:", "total sts:",
"sod:", "gifted:", "lesson title:", "textbook:", "chapter/unit:", "strand/domain:",
"topic:", "essential question/big idea:", "standards:", "objectives:", "integration:",
"academic/concept key vocabulary:", "connections to real life/emirati identity:",
"learner’s profile/sustainability attributes:", "moral education learning outcomes:",
"warm up", "body of the lesson", "plenary", "assessment", "homework", "pre-assessment", "post-assessment",
"warm-up", "body of lesson", "plenary / assessment", "teacher notes / reflection",
"instructional materials & resources", "critical thinking skills", "science, engineering, & mathematical practices involved",
"pre-assessment questions", "post-assessment questions",
"challenge for gifted and outstanding students", "task for sod student", "group task", "success criteria",
"explore/discover", "model/explain", "activity:", "guided practice", "independent task",
"lesson explanation", # Added from presentation slide 6 title
"challenge for gifted and sod student", # Added from presentation slide 7 title
"group task", # Added from presentation slide 8 title
# Added more general section starters to improve stopping
"materials", "resources", "procedure", "introduction", "main activity", "conclusion",
"differentiation", "extension activities", "reflection", "summary", "key concepts",
"vocabulary", "assessment strategies", "lesson flow", "steps", "task", "activity",
"notes for teacher", "teacher guidance", "student task", "student activity", "learning activities",
"lesson plan", "lesson overview", "lesson outline", "lesson procedure"
]
# Create a lookahead pattern for any of the terminators, making them robust to line breaks and case.
# We need to match actual section headers, which typically start on a new line.
# (?:^|\n\s*) ensures it matches at the very beginning of the string or after newlines (with optional leading space)
# re.escape makes sure special regex characters in headers are treated literally.
# (?::|\b) makes sure it matches "Standards" or "Standards:"
stop_pattern = '|'.join([rf"(?:\n\s*|^){re.escape(h.lower())}(?::|\b)" for h in all_section_headers])
# The pattern: Look for the target header, then capture content (non-greedy)
# until the *beginning of a new line* that matches any of the section headers, or the end of the string.
# Using re.IGNORECASE | re.DOTALL | re.MULTILINE for proper matching.
pattern = rf"({combined_header_pattern})\s*(.*?)(?=(?:\n\s*|^)(?:{stop_pattern})|$)"
match = re.search(pattern, text, re.IGNORECASE | re.DOTALL | re.MULTILINE)
if match:
content = match.group(2).strip()
# Clean up common list prefixes if they are still present
content = re.sub(r'^\s*[\*-•]\s*|\d+\.\s*', '', content, flags=re.MULTILINE)
return content
return ""
def parse_lesson_details_from_text(first_page_text, full_text):
"""
Parses text to extract lesson plan details. It uses AI to infer the lesson
title and topic from the first page's content if they are not explicitly labeled.
"""
extracted_data = {}
# --- AI-Powered Title and Topic Inference ---
if first_page_text and "Error:" not in first_page_text:
try:
# Use a targeted AI prompt to get just the title and topic
prompt = f"""
**PRIORITY INSTRUCTION: Use the extracted details from the uploaded PDF as the primary source of information.
Only generate new content for sections where extracted details are missing or incomplete.**
Analyze the following text from the first page of a lesson presentation.
Based ONLY on this text, identify the most likely "Lesson Title" and "Topic".
The "Lesson Title" should be the main, overarching title.
The "Topic" should be a more specific subject within that title.
Respond in a simple "Title: [Your Inferred Title]\nTopic: [Your Inferred Topic]" format.
First Page Text:
"{first_page_text}"
"""
chat_completion = groq_client.chat.completions.create(
messages=[{"role": "user", "content": prompt}],
model="llama3-8b-8192", # Corrected model name
temperature=0.2,
)
ai_response = chat_completion.choices[0].message.content
# Parse the AI's response
title_match = re.search(r"Title:\s*(.*)", ai_response, re.IGNORECASE)
topic_match = re.search(r"Topic:\s*(.*)", ai_response, re.IGNORECASE)
if title_match:
extracted_data["lesson_title"] = title_match.group(1).strip()
if topic_match:
extracted_data["topic"] = topic_match.group(1).strip()
except Exception as e:
print(f"AI inference for title/topic failed: {e}")
extracted_data["lesson_title"] = ""
extracted_data["topic"] = ""
# --- End AI-Powered Title and Topic Inference ---
norm_text = full_text.lower()
# --- Extract Learning Objectives using the improved function ---
objective_headers = [
"Learning Objectives", "Learning Objective", "Objectives", "Objective", "LO:", "LO"
]
extracted_objectives = extract_list_content(objective_headers, norm_text)
# Reformat as bullet points if it's a block of text, to match expected output for Gradio UI
if extracted_objectives:
# Split by common newline patterns and add bullet points
lines = [line.strip() for line in re.split(r'\n{1,}', extracted_objectives) if line.strip()]
extracted_data["learning_objectives"] = "\n".join([f"- {line}" for line in lines])
else:
extracted_data["learning_objectives"] = ""
# Define a broader set of terminators for the general extract_field.
# Note: extract_list_content uses its own set of terminators relevant to lists.
general_terminators = [
"subject:", "class/section:", "date:", "term:", "school:", "teacher’s name:", "total sts:",
"sod:", "gifted:", "lesson title:", "textbook:", "chapter/unit:", "strand/domain:",
"topic:", "essential question/big idea:", "standards:", "objectives:", "integration:",
"academic/concept key vocabulary:", "connections to real life/emirati identity:",
"learner’s profile/sustainability attributes:", "moral education learning outcomes:",
"warm up", "body of the lesson" # These are general section headers
]
general_stop_pattern = '|'.join([re.escape(t) for t in general_terminators])
def extract_field(header, text):
# This extract_field is for single-line entries or short phrases
pattern = rf"{re.escape(header)}\s*(.*?)(?={general_stop_pattern}|$)"
match = re.search(pattern, text, re.IGNORECASE | re.DOTALL)
if match:
return match.group(1).strip()
return ""
# --- General Field Extractions (can be kept as is, using the original extract_field) ---
extracted_data["standards"] = extract_field("standards:", norm_text)
extracted_data["subject"] = extract_field("subject:", norm_text).title() # Use title() for proper casing
# Add other field extractions here as before, using the general extract_field
extracted_data["class_section"] = extract_field("class/section:", norm_text)
extracted_data["date"] = extract_field("date:", norm_text)
extracted_data["term_week"] = extract_field("term:", norm_text) or extract_field("week:", norm_text) or extract_field("term/week:", norm_text)
extracted_data["school"] = extract_field("school:", norm_text)
extracted_data["teacher_name"] = extract_field("teacher’s name:", norm_text)
extracted_data["total_students"] = extract_field("total sts:", norm_text)
extracted_data["sod_student"] = extract_field("sod:", norm_text)
extracted_data["gifted_student"] = extract_field("gifted:", norm_text)
# The AI handles lesson_title and topic, but you can keep these as fallbacks if AI fails
# extracted_data["lesson_title"] = extract_field("lesson title:", norm_text)
# extracted_data["topic"] = extract_field("topic:", norm_text)
extracted_data["textbook"] = extract_field("textbook:", norm_text)
extracted_data["chapter_unit"] = extract_field("chapter/unit:", norm_text)
extracted_data["strand_domain"] = extract_field("strand/domain:", norm_text)
extracted_data["essential_question"] = extract_field("essential question/big idea:", norm_text) # New: Extract essential question
extracted_data["integration"] = extract_field("integration:", norm_text)
extracted_data["vocabulary"] = extract_field("academic/concept key vocabulary:", norm_text)
extracted_data["connections"] = extract_field("connections to real life/emirati identity:", norm_text)
extracted_data["learner_profile"] = extract_field("learner’s profile/sustainability attributes:", norm_text)
extracted_data["moral_education"] = extract_field("moral education learning outcomes:", norm_text)
# You might want to clean up trailing "empty" values or specific placeholders like "-"
for key, value in extracted_data.items():
if value and value.strip() in ["-", "n/a", "none"]: # Add more such placeholders if common
extracted_data[key] = ""
return extracted_data
def autofill_from_reference_pdf(pdf_file_obj):
"""
Event handler for PDF upload to autofill fields in the Gradio UI.
Returns values for each input component to update them.
"""
default_values = {
"subject": "ICT/Computer Science", "grade": "4", "class_section": "A/B/C/D",
"date": "28th May, 2025", "term_week": "Term 1- Week 1", "school": "Elementary",
"teacher_name": "Ms. Nazish Hasan", "total_students": "24",
"sod_student": "4A: Hazza", "gifted_student": "4C: Julia",
"lesson_title": "-", "textbook": "-", "chapter_unit": "4",
"standards": "3-5.IC.20", "learning_objectives": "I can...", "strand_domain": "-", "topic": "-",
# These are now AI generated for the docx, so default them here for autofill purposes.
# If the PDF contains them, they will be extracted.
"essential_question": "", "integration": "", "vocabulary": "", "connections": "", "learner_profile": "", "moral_education": ""
}
if pdf_file_obj is None:
return (default_values["subject"], default_values["grade"], default_values["class_section"],
default_values["date"], default_values["term_week"], default_values["school"],
default_values["teacher_name"], default_values["total_students"], default_values["sod_student"],
default_values["gifted_student"], default_values["lesson_title"], default_values["textbook"],
default_values["chapter_unit"], default_values["standards"], default_values["learning_objectives"],
default_values["strand_domain"], default_values["topic"],
"No PDF uploaded or extracted data yet.")
pdf_path = pdf_file_obj.name
first_page_text, full_text = extract_text_from_pdf(pdf_path)
if "Error:" in full_text:
gr.Warning(full_text)
return (default_values["subject"], default_values["grade"], default_values["class_section"],
default_values["date"], default_values["term_week"], default_values["school"],
default_values["teacher_name"], default_values["total_students"], default_values["sod_student"],
default_values["gifted_student"], default_values["lesson_title"], default_values["textbook"],
default_values["chapter_unit"], default_values["standards"], default_values["learning_objectives"],
default_values["strand_domain"], default_values["topic"],
full_text)
parsed_data = parse_lesson_details_from_text(first_page_text, full_text)
# This check for "Error:" in full_text is redundant if the previous one caught it.
# If parse_lesson_details_from_text can return an error string,
# you might want to check parsed_data for an error key/value.
if "Error:" in full_text: # Keeping this for now as it was in previous user code, but it's redundant.
gr.Warning(full_text)
return (default_values["subject"], default_values["grade"], default_values["class_section"],
default_values["date"], default_values["term_week"], default_values["school"],
default_values["teacher_name"], default_values["total_students"], default_values["sod_student"],
default_values["gifted_student"], default_values["lesson_title"], default_values["textbook"],
default_values["chapter_unit"], default_values["standards"], default_values["learning_objectives"],
default_values["strand_domain"], default_values["topic"],
full_text)
formatted_parsed_data = "--- Extracted Data from PDF ---\n"
found_any_specific_data = False
for key, value in parsed_data.items():
# Only show parsed data that is not empty and not the default value
if value and value.strip() != "" and value != default_values.get(key, ""):
formatted_parsed_data += f"{key.replace('_', ' ').title()}: {value}\n"
found_any_specific_data = True
if not found_any_specific_data:
formatted_parsed_data += "No specific lesson plan details could be extracted using current parsing rules.\n"
formatted_parsed_data += "\n--- Raw Text from PDF ---\n" + full_text
return (parsed_data.get("subject") or default_values["subject"],
parsed_data.get("grade") or default_values["grade"],
parsed_data.get("class_section") or default_values["class_section"],
parsed_data.get("date") or default_values["date"],
parsed_data.get("term_week") or default_values["term_week"],
parsed_data.get("school") or default_values["school"],
parsed_data.get("teacher_name") or default_values["teacher_name"],
parsed_data.get("total_students") or default_values["total_students"],
parsed_data.get("sod_student") or default_values["sod_student"],
parsed_data.get("gifted_student") or default_values["gifted_student"],
parsed_data.get("lesson_title") or default_values["lesson_title"],
parsed_data.get("textbook") or default_values["textbook"],
parsed_data.get("chapter_unit") or default_values["chapter_unit"],
parsed_data.get("standards") or default_values["standards"],
parsed_data.get("learning_objectives") or default_values["learning_objectives"], # Fixed typo here in case it existed
parsed_data.get("strand_domain") or default_values["strand_domain"],
parsed_data.get("topic") or default_values["topic"],
formatted_parsed_data)
# --- Refactored AI Content Generation Function ---
def generate_ai_content(
subject, grade, class_section, date, term_week, school, teacher_name,
total_students, sod_student, gifted_student, lesson_title, textbook,
chapter_unit, standards, learning_objectives, strand_domain, topic,
additional_ai_requirements
):
"""
Uses Groq API to generate the rest of the lesson plan content.
The output is structured with delimiters for parsing.
"""
ai_generated_content = ""
# Initialize all sections to empty string, including new ones
parsed_ai_sections = {
"essential_question": "",
"integration": "",
"vocabulary": "",
"connections": "",
"learner_profile": "",
"moral_education": "",
"warm_up_procedure": "",
"pre_assessment_questions": "",
"body_of_lesson_procedure": "",
"success_criteria": "",
"challenge_gifted_ai": "",
"task_sod_ai": "",
"group_task_ai": "",
"plenary_assessment_details": "",
"post_assessment_questions": "",
"instructional_materials": "", # New
"critical_thinking": "", # New
"teacher_notes_homework": "", # New
}
try:
# Prompt from user's request
ai_prompt = f"""
You are an Educational AI assistant specialized in generating detailed lesson plan content. Based on the following main details,
generate the rest of a comprehensive lesson plan.
Structure your output using the following delimiters for main sections:
##INTEGRATION##
##VOCABULARY##
##ESSENTIAL_QUESTION##
##CONNECTIONS##
##LEARNER_PROFILE##
##MORAL_EDUCATION##
##INSTRUCTIONAL_MATERIALS##
##CRITICAL_THINKING##
##WARM_UP_PROCEDURE##
##BODY_OF_LESSON_PROCEDURE##
##PLENARY_ASSESSMENT_DETAILS##
##TEACHER_NOTES_HOMEWORK##
##PRE_ASSESSMENT_QUESTIONS##
##POST_ASSESSMENT_QUESTIONS##
Within ##BODY_OF_LESSON_PROCEDURE##, use these sub-delimiters for special tasks
##CHALLENGE_GIFTED_AI##
##TASK_SOD_AI##
##GROUP_TASK_AI##
##SUCCESS_CRITERIA##
---
Main Details Provided:
Subject: {subject}
Class/Section: {class_section}
Date: {date}
Term/Week: {term_week}
School: {school}
Teacher’s Name: {teacher_name}
TOTAL Students: {total_students}
SoD: {sod_student}
Gifted: {gifted_student}
Lesson Title: {lesson_title}
Textbook: {textbook}
Chapter/Unit: {chapter_unit}
Standards: {standards}
Strand/Domain: {strand_domain}
Topic: {topic}
Learning Objectives: {learning_objectives}
Additional AI Requirements: {additional_ai_requirements if additional_ai_requirements else "None"}
---
Generate the detailed content for the following sections based on the provided details, ensuring each section is followed by its specific delimiter. If a section is not applicable or cannot be generated, leave its content blank but keep the delimiter.
Integration:
##INTEGRATION##
(Integrated any other 2 subjects to the current lesson topic and learning objectives. write the name of subject in capital letters and then Generate 4 to 5 lines for each subject, divide them in paragraphs.)
Academic/Concept Key Vocabulary:
##VOCABULARY##
(List 3-5 key vocabulary words related to the lesson title, subject, strand, and topic. Use bullet points.)
Essential Question/Big Idea:
##ESSENTIAL_QUESTION##
(Generate an essential question/big idea based on the lesson title, subject, and topic.)
Connections to Real Life/Emirati Identity:
##CONNECTIONS##
(Provide a detailed description of how this lesson connects to real life and Emirati identity.
Include concrete examples or activities. Use bullet points for key connections. Based on the following inputs: Subject, Class, Title and Learning Objectives, Connection the lesson to UAE National Identity. Identify one of the three domains of UAE National Identity: Citizenship, Culture, or Values. Suggest a hands-on activity that will engage students and reinforce the connection to UAE National Identity, providing clear instructions and materials needed for the activity.)
Learner’s Profile/Sustainability Attributes:
##LEARNER_PROFILE##
(List 3-4 relevant learner attributes with brief descriptions, using bullet points.)
Moral Education Learning Outcomes:
##MORAL_EDUCATION##
(List 3 relevant moral education outcomes with brief descriptions, using bullet points.)
Instructional Materials & Resources:
##INSTRUCTIONAL_MATERIALS##
(Based on the subject, generate a list of relevant materials and resources. Specify quantities or types where appropriate. For Technology-related lessons, suggest a specific digital tool or platform.)
Critical Thinking Skills / Science, Engineering, & Mathematical Practices Involved:
Students will:
##CRITICAL_THINKING##
(Provide a brief explanation of how the lesson involves critical thinking skills and practices. Use action verbs and describe student engagement.)
WARM UP (STARTER ACTIVITY):
##WARM_UP_PROCEDURE##
(Time: 5 min. Provide a brief, engaging warm-up procedure based on the lesson title and topic. Describe the activity clearly, mentioning whether it's group/individual/pair work.)
BODY OF THE LESSON:
Time: 10 mins
##BODY_OF_LESSON_PROCEDURE##
EXPLORE/DISCOVER:
(Explanation of the concept here. ~50-70 words)
MODEL/EXPLAIN:
(Demonstrate the skill, How it works. ~50-70 words)
Activity:
Instructions for the UAE National Identity Activity (15 minutes):
(Provide clear, step-by-step instructions for the main activity, using numbered steps.)
Guided Practice (5 minutes):
(Describe how the teacher will support students, e.g., 'Walk around the classroom, providing individual assistance...', ~30-50 words)
Independent Task (15 minutes):
(Provide three distinct and clearly defined independent tasks for students of varying ability levels within a lesson plan framework. Each task should be tailored to the respective ability level and should specify the exact actions students must take to complete the task.- **Requirements:** 1.**High Ability Task:** - Define a task that encourages higher-order thinking skills.- Specify the expected output, such as a written report, presentation, or project.- Include at least two specific criteria or components that students must incorporate, ensuring that the task challenges their critical thinking and creativity.2.**Medium Ability Task:** - Create a task that requires application of concepts learned in class.- Clearly outline the steps students need to follow to complete the task, including any materials or resources they may need.- Include one or two guiding questions or prompts to aid their understanding and execution of the task.3.**Low Ability Task:** - Design a task that allows for practice of foundational skills or concepts.- Provide explicit instructions on what students must do, such as completing a worksheet, drawing a picture, or matching items.- Ensure that the task is straightforward and includes any necessary supports or examples to assist students in completing the activity successfully.- **Format:** Present the tasks in a bulleted list with clear headings for each ability level.)
1. High Ability Task:
2. Medium Ability Task:
3. Low Ability Task:
Challenge for Gifted and outstanding Students: ({gifted_student})
##CHALLENGE_GIFTED_AI##
(MUST provide a challenging, multi-step task for the gifted student. Use bullet points.)
Task for SOD Student (Autism-Friendly for {sod_student}):
##TASK_SOD_AI##
(MUST provide a simplified, clear, and autism-friendly task for the SoD student. Use bullet points.)
Group Task: "Proud to be UAE!" Collaborative task for the groups of 4
Instructions for Students:
##GROUP_TASK_AI##
(MUST provide detailed, bulleted instructions for a collaborative group task for groups of 4.)
Success Criteria:
##SUCCESS_CRITERIA##
(You are tasked with generating clear and achievable success criteria for three independent tasks designed for students of varying abilities. Each task will be categorized as high, medium, or low ability. For each task, please provide specific criteria that align with grade-level expectations and can be easily understood and accomplished by students.
1. **High Ability Task Success Criteria:**
- Clearly outline three specific success criteria that challenge high-ability students, encouraging critical thinking and deep understanding of the subject matter. Ensure that these criteria foster independence and creativity in problem-solving.
2. **Medium Ability Task Success Criteria:**
- Provide three success criteria that are attainable for medium-ability students, focusing on comprehension and application of concepts. These criteria should promote confidence and skill development, allowing students to demonstrate their understanding effectively.
3. **Low Ability Task Success Criteria:**
- Generate three success criteria tailored for low-ability students, emphasizing foundational skills and basic understanding. These criteria should be straightforward and provide a clear path to success, ensuring that students can complete tasks with minimal frustration.
Make sure each set of criteria is distinctive and caters to the specific needs of the ability group it is intended for, while promoting a sense of achievement in students.)
PLENARY / ASSESSMENT:
##PLENARY_ASSESSMENT_DETAILS##
(Time: 5 mins. Describe the plenary and assessment strategy. Include a brief procedure for evaluating student understanding.)
TEACHER NOTES/REFLECTION:
##TEACHER_NOTES_HOMEWORK##
(Provide notes for the teacher regarding the lesson delivery, and assign relevant homework.)
PRE-ASSESSMENT Questions:
##PRE_ASSESSMENT_QUESTIONS##
(Generate 2-3 brief pre-assessment questions related to the lesson objectives to gauge prior knowledge. Use numbered list.)
POST-ASSESSMENT Questions:
##POST_ASSESSMENT_QUESTIONS##
(Generate 2-3 brief post-assessment questions to check understanding after the lesson. Use numbered list.)
"""
arabic_subjects = ["Arabic", "Islamic Studies", "Moral Education"]
if subject in arabic_subjects:
ai_prompt += "\n\n**IMPORTANT: Generate ALL the content for the lesson plan in Arabic language.**"
ai_prompt += "\n\n**ملاحظة هامة: يجب إنشاء كل محتوى خطة الدرس باللغة العربية.**"
chat_completion = groq_client.chat.completions.create(
messages=[{"role": "user", "content": ai_prompt}],
model="llama3-8b-8192",
temperature=0.7,
max_tokens=2048,
top_p=1,
stop=None,
stream=False,
)
ai_generated_content = chat_completion.choices[0].message.content
# Define all possible delimiters and their corresponding keys
all_known_delimiters = [
("##INTEGRATION##", "integration"),
("##VOCABULARY##", "vocabulary"),
("##ESSENTIAL_QUESTION##", "essential_question"),
("##CONNECTIONS##", "connections"),
("##LEARNER_PROFILE##", "learner_profile"),
("##MORAL_EDUCATION##", "moral_education"),
("##INSTRUCTIONAL_MATERIALS##", "instructional_materials"),
("##CRITICAL_THINKING##", "critical_thinking"),
("##WARM_UP_PROCEDURE##", "warm_up_procedure"),
("##BODY_OF_LESSON_PROCEDURE##", "body_of_lesson_procedure"),
("##PLENARY_ASSESSMENT_DETAILS##", "plenary_assessment_details"),
("##TEACHER_NOTES_HOMEWORK##", "teacher_notes_homework"),
("##PRE_ASSESSMENT_QUESTIONS##", "pre_assessment_questions"),
("##POST_ASSESSMENT_QUESTIONS##", "post_assessment_questions"),
# Sub-delimiters that are expected within BODY_OF_LESSON_PROCEDURE
("##CHALLENGE_GIFTED_AI##", "challenge_gifted_ai"),
("##TASK_SOD_AI##", "task_sod_ai"),
("##GROUP_TASK_AI##", "group_task_ai"),
("##SUCCESS_CRITERIA##", "success_criteria"),
]
# Use a dictionary for quick lookup of key from delimiter string
delimiter_to_key_map = {delim_str: key for delim_str, key in all_known_delimiters}
# Regex to find any of the delimiters
full_delimiters_regex = "|".join(re.escape(d[0]) for d in all_known_delimiters)
# Split the AI generated content by all delimiters, keeping the delimiters in the result
parts = re.split(f"({full_delimiters_regex})", ai_generated_content)
current_section_key = None
main_body_content_raw = "" # To store the raw content of BODY_OF_LESSON_PROCEDURE
# Iterate through parts and assign content to respective sections
for i in range(len(parts)):
part = parts[i].strip()
if not part:
continue
if part in delimiter_to_key_map:
current_section_key = delimiter_to_key_map[part]
else:
if current_section_key:
if current_section_key == "body_of_lesson_procedure":
main_body_content_raw = part # Capture raw body content for sub-parsing
parsed_ai_sections[current_section_key] = part
current_section_key = None # Reset after content is assigned
# --- Sub-parsing for BODY_OF_LESSON_PROCEDURE ---
if "body_of_lesson_procedure" in parsed_ai_sections and parsed_ai_sections["body_of_lesson_procedure"]:
# The initially captured 'body_of_lesson_procedure' content might contain sub-delimiters and their content.
# We need to extract these sub-sections and then clean the main body content.
# Define sub-section delimiters for internal parsing
sub_section_delimiters_in_body = [
("##CHALLENGE_GIFTED_AI##", "challenge_gifted_ai"),
("##TASK_SOD_AI##", "task_sod_ai"),
("##GROUP_TASK_AI##", "group_task_ai"),
("##SUCCESS_CRITERIA##", "success_criteria"),
]
sub_delimiter_to_key_map = {delim_str: key for delim_str, key in sub_section_delimiters_in_body}
sub_delimiters_regex = "|".join(re.escape(d[0]) for d in sub_section_delimiters_in_body)
sub_parts = re.split(f"({sub_delimiters_regex})", main_body_content_raw)
cleaned_main_body_parts = []
current_sub_section_key = None
for i in range(len(sub_parts)):
sub_part = sub_parts[i].strip()
if not sub_part:
continue
if sub_part in sub_delimiter_to_key_map:
current_sub_section_key = sub_delimiter_to_key_map[sub_part]
else:
if current_sub_section_key:
parsed_ai_sections[current_sub_section_key] = sub_part
current_sub_section_key = None # Reset
else:
# This content is part of the main body procedure (not a sub-section)
cleaned_main_body_parts.append(sub_part)
# Reconstruct the cleaned main body content
parsed_ai_sections["body_of_lesson_procedure"] = "\n".join(cleaned_main_body_parts).strip()
except Exception as e:
ai_generated_content = f"Error generating or parsing AI content: {e}"
gr.Warning(ai_generated_content) # Show warning in UI
return parsed_ai_sections, ai_generated_content
return parsed_ai_sections, ai_generated_content
# --- AI Document Creation Function ---
def create_document_from_inputs_and_ai(
subject, grade, class_section, date, term_week, school, teacher_name,
total_students, sod_student, gifted_student, lesson_title, textbook,
chapter_unit, standards, learning_objectives, strand_domain, topic,
additional_ai_requirements
):
# Load the template
try:
document = Document(LP_TEMPLATE_PATH)
except Exception as e:
# Corrected: Return gr.File.update with None for the file output on error
return gr.File.update(value=None, filename=None), gr.Error(f"Error loading document template: {e}")
# Collect all input data into a map for easy replacement
data_map = {
"##SUBJECT##": subject,
"##GRADE##": grade,
"##CLASS/SECTION##": class_section,
"##DATE##": date,
"##TERM-WEEK##": term_week,
"##SCHOOL##": school,
"##TEACHERNAME##": teacher_name,
"##TOTAL-STUDENTS##": total_students,
"##SOD-STUDENT##": sod_student,
"##GIFTED-STUDENT##": gifted_student,
"##LESSON-TITLE##": lesson_title,
"##TEXTBOOK##": textbook,
"##CHAPTER/UNIT##": chapter_unit,
"##STANDARDS##": standards,
"##LEARNING-OBJECTIVES##": learning_objectives,
"##STRAND/DOMAIN##": strand_domain,
"##TOPIC##": topic,
}
# First pass for static inputs
for paragraph in document.paragraphs:
replace_placeholders_in_paragraph(paragraph, data_map)
for table in document.tables:
replace_placeholders_in_table(table, data_map)
set_table_borders_and_shading(table, shade_color='D3D3D3') # Apply shading
# --- Call the new generate_ai_content function ---
parsed_ai_sections, ai_raw_output = generate_ai_content(
subject, grade, class_section, date, term_week, school, teacher_name,
total_students, sod_student, gifted_student, lesson_title, textbook,
chapter_unit, standards, learning_objectives, strand_domain, topic,
additional_ai_requirements
)
# Add parsed AI content to data_map for replacement
data_map["##ESSENTIAL_QUESTION##"] = parsed_ai_sections["essential_question"]
data_map["##INTEGRATION##"] = parsed_ai_sections["integration"]
data_map["##VOCABULARY##"] = parsed_ai_sections["vocabulary"]
data_map["##CONNECTIONS##"] = parsed_ai_sections["connections"]
data_map["##LEARNER_PROFILE##"] = parsed_ai_sections["learner_profile"]
data_map["##MORAL_EDUCATION##"] = parsed_ai_sections["moral_education"]
data_map["##WARM_UP_PROCEDURE##"] = parsed_ai_sections["warm_up_procedure"]
data_map["##PRE_ASSESSMENT_QUESTIONS##"] = parsed_ai_sections["pre_assessment_questions"]
data_map["##BODY_OF_LESSON_PROCEDURE##"] = parsed_ai_sections["body_of_lesson_procedure"]
data_map["##SUCCESS_CRITERIA##"] = parsed_ai_sections["success_criteria"]
data_map["##CHALLENGE_GIFTED_AI##"] = parsed_ai_sections["challenge_gifted_ai"]
data_map["##TASK_SOD_AI##"] = parsed_ai_sections["task_sod_ai"]
data_map["##GROUP_TASK_AI##"] = parsed_ai_sections["group_task_ai"]
data_map["##PLENARY_ASSESSMENT_DETAILS##"] = parsed_ai_sections["plenary_assessment_details"]
data_map["##POST_ASSESSMENT_QUESTIONS##"] = parsed_ai_sections["post_assessment_questions"]
data_map["##INSTRUCTIONAL_MATERIALS##"] = parsed_ai_sections["instructional_materials"]
data_map["##CRITICAL_THINKING##"] = parsed_ai_sections["critical_thinking"]
data_map["##TEACHER_NOTES_HOMEWORK##"] = parsed_ai_sections["teacher_notes_homework"]
# Re-run placeholder replacement to fill AI-generated content
for paragraph in document.paragraphs:
replace_placeholders_in_paragraph(paragraph, data_map)
for table in document.tables:
replace_placeholders_in_table(table, data_map)
# Save the document to a BytesIO object
docx_io = BytesIO()
document.save(docx_io)
docx_io.seek(0)
# Sanitize lesson title for filename
with tempfile.NamedTemporaryFile(suffix=".docx", delete=False) as temp_file:
document.save(temp_file.name)
docx_path = temp_file.name
sanitized_lesson_title = _sanitize_filename(lesson_title)
output_filename = f"{sanitized_lesson_title}_Lesson_Plan.docx"
# Optionally rename the file to the desired filename
new_path = os.path.join(os.path.dirname(docx_path), output_filename)
os.rename(docx_path, new_path)
return new_path, ai_raw_output
#return {"name": output_filename, "data": docx_io.getvalue()}, ai_raw_output
# Corrected return: Use gr.File.update to provide binary content and filename
#return gr.File.update(value=docx_io.getvalue(), filename=output_filename), ai_raw_output
#return docx_io.getvalue(), ai_raw_output
#return gr.File.update(value=docx_io.getvalue(), filename=output_filename), ai_raw_output
#return (docx_io.getvalue(), output_filename), ai_raw_output
# --- NEW: Presentation Creation Function ---
def create_presentation_from_inputs_and_ai(
subject, grade, class_section, date, term_week, school, teacher_name,
total_students, sod_student, gifted_student, lesson_title, textbook,
chapter_unit, standards, learning_objectives, strand_domain, topic,
additional_ai_requirements # Removed specific AI-generated inputs as they aren't directly mapped to PPTX
):
prs = Presentation() # Start with a blank presentation
# --- Title Slide ---
title_slide_layout = prs.slide_layouts[0] # Usually the title slide layout
slide = prs.slides.add_slide(title_slide_layout)
title = slide.shapes.title
subtitle = slide.placeholders[1]
title.text = lesson_title if lesson_title else "Lesson Presentation"
subtitle.text = f"{subject} - Grade {grade}\nTeacher: {teacher_name}\nDate: {date}"
# Customize title and subtitle fonts/colors (Optional)
for run in title.text_frame.paragraphs[0].runs:
run.font.size = Pt(44)
run.font.color.rgb = RGBColor(0x00, 0x00, 0x00) # Black
for run in subtitle.text_frame.paragraphs[0].runs:
run.font.size = Pt(20)
run.font.color.rgb = RGBColor(0x33, 0x33, 0x33) # Dark gray
# --- AI Content Generation for Presentation Slides ---
ai_generated_pptx_content = ""
try:
pptx_prompt = f"""
Generate content for a presentation based on the following lesson plan details.
Structure the output into distinct slides. Each slide should have a clear title and bullet points or short paragraphs for content.
Do NOT include an introduction or conclusion slide unless specifically requested.
Focus on the core components of a lesson: Warm-up, Lesson Explanation/Body, Challenge/Activities, and Plenary/Assessment.
**Subject:** {subject}
**Grade:** {grade}
**Lesson Title:** {lesson_title}
Learning Objectives: {learning_objectives}
Standards: {standards}
Topic: {topic}
Additional Information/Requirements: {additional_ai_requirements if additional_ai_requirements else "None specified."}
Include content related to:
- Essential Question
- Integration
- Academic/Concept Key Vocabulary
- Connections to Real Life/Emirati Identity
- Learner’s Profile/Sustainability Attributes
- Moral Education Learning Outcomes
Provide the output in the following markdown-like format for easy parsing:
## Slide Title 1
- Point 1
- Point 2
## Slide Title 2
- Point A
- Point B
... and so on.
"""
chat_completion = groq_client.chat.completions.create(
messages=[{"role": "user", "content": pptx_prompt}],
model="llama3-8b-8192", # Corrected model name
temperature=0.7,
)
ai_generated_pptx_content = chat_completion.choices[0].message.content
# Parse AI generated content into slides
slides_data = re.split(r'##\s*', ai_generated_pptx_content)[1:] # Split by '##' and remove empty first element
for slide_block in slides_data:
if not slide_block.strip():
continue
lines = slide_block.strip().split('\n')
slide_title = lines[0].strip()
slide_content = "\n".join(lines[1:]).strip()
# Add a new slide (using a common title and content layout)
bullet_slide_layout = prs.slide_layouts[1]
slide = prs.slides.add_slide(bullet_slide_layout)
# Set title
title = slide.shapes.title
title.text = slide_title
# Set content
body = slide.shapes.placeholders[1]
tf = body.text_frame
tf.clear() # Clear existing text in text frame
# Add bullet points
for line in slide_content.split('\n'):
line = line.strip()
if line: # Only add non-empty lines
p = tf.add_paragraph()
p.text = line.lstrip('*- ').strip() # Remove common bullet characters
p.level = 1 # Set indentation level for bullet points
p.font.size = Pt(18)
p.space_after = Pt(8) # Add some space after each point
# For "I can..." statements, try to bold them or make them distinct (Optional)
if p.text.lower().startswith("i can"):
for run in p.runs:
run.font.bold = True
run.font.color.rgb = RGBColor(0x00, 0x56, 0x8C) # A blue color
except Exception as e:
ai_generated_pptx_content = f"Error generating AI presentation content: {e}"
# Add an error slide if AI generation fails
error_slide_layout = prs.slide_layouts[1]
slide = prs.slides.add_slide(error_slide_layout)
title = slide.shapes.title
title.text = "Presentation Generation Error"
body = slide.shapes.placeholders[1]
tf = body.text_frame
p = tf.add_paragraph()
p.text = ai_generated_pptx_content
p.font.size = Pt(18)
# Save the presentation to a BytesIO object
pptx_io = BytesIO()
prs.save(pptx_io)
pptx_io.seek(0)
with tempfile.NamedTemporaryFile(suffix=".pptx", delete=False) as temp_file:
prs.save(temp_file.name)
pptx_path = temp_file.name
# Sanitize lesson title for filename
sanitized_lesson_title = _sanitize_filename(lesson_title)
output_filename = f"{sanitized_lesson_title}_Presentation.pptx"
new_path = os.path.join(os.path.dirname(pptx_path), output_filename)
os.rename(pptx_path, new_path)
return new_path, ai_generated_pptx_content
# Add this new function above the Gradio interface section
def generate_lesson_plan_from_pdf(
pdf_file_obj,
additional_ai_requirements
):
"""Generate lesson plan directly from uploaded PDF file"""
# Extract text from PDF
if not pdf_file_obj:
return None, "No PDF file uploaded"
pdf_path = pdf_file_obj.name
first_page_text, full_text = extract_text_from_pdf(pdf_path)
if "Error:" in full_text:
return None, full_text
# Parse lesson details from PDF text
parsed_data = parse_lesson_details_from_text(first_page_text, full_text)
# Prepare data for AI generation, using extracted values where available
subject = parsed_data.get("subject", "ICT/Computer Science")
grade = parsed_data.get("grade", "4")
class_section = parsed_data.get("class_section", "A/B/C/D")
date = parsed_data.get("date", "28th May, 2025")
term_week = parsed_data.get("term_week", "Term 1- Week 1")
school = parsed_data.get("school", "Elementary")
teacher_name = parsed_data.get("teacher_name", "Ms. Nazish Hasan")
total_students = parsed_data.get("total_students", "24")
sod_student = parsed_data.get("sod_student", "4A: Hazza")
gifted_student = parsed_data.get("gifted_student", "4C: Julia")
lesson_title = parsed_data.get("lesson_title", "Introduction to Programming")
textbook = parsed_data.get("textbook", "Computer Science for Kids")
chapter_unit = parsed_data.get("chapter_unit", "Chapter 4: Algorithms")
standards = parsed_data.get("standards", "3-5.IC.20")
learning_objectives = parsed_data.get("learning_objectives", "I can...")
strand_domain = parsed_data.get("strand_domain", "Computational Thinking")
topic = parsed_data.get("topic", "Algorithms")
# Generate document using extracted details
return create_document_from_inputs_and_ai(
subject, grade, class_section, date, term_week, school, teacher_name,
total_students, sod_student, gifted_student, lesson_title, textbook,
chapter_unit, standards, learning_objectives, strand_domain, topic,
additional_ai_requirements
)
# --- Gradio Interface ---
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("# AI Lesson Planner & Presentation Generator")
gr.Markdown("Upload a reference PDF to autofill details, then refine and generate your lesson plan and presentation.")
with gr.Row():
with gr.Column():
pdf_file_input = gr.File(label="Upload Reference PDF (Optional)", type="filepath")
autofill_btn = gr.Button("Autofill from PDF")
extracted_details_textbox = gr.Textbox(label="Extracted Details (for debugging)", lines=10, interactive=False)
gr.Markdown("### Lesson Plan Details")
subject_input = gr.Dropdown(label="Subject", choices=SUBJECTS, value="ICT/Computer Science")
grade_input = gr.Dropdown(label="Grade", choices=GRADES, value="4")
class_section_input = gr.Textbox(label="Class/Section", value="A/B/C/D")
date_input = gr.Textbox(label="Date (e.g., 28th May, 2025)", value="28th May, 2025")
term_week_input = gr.Textbox(label="Term & Week (e.g., Term 1- Week 1)", value="Term 1- Week 1")
school_input = gr.Textbox(label="School", value="Elementary")
teacher_name_input = gr.Textbox(label="Teacher's Name", value="Ms. Nazish Hasan")
total_students_input = gr.Textbox(label="Total Students", value="24")
sod_student_input = gr.Textbox(label="SoD Student (Student of Determination)", value="4A: Hazza")
gifted_student_input = gr.Textbox(label="Gifted Student", value="4C: Julia")
lesson_title_input = gr.Textbox(label="Lesson Title", value="Introduction to Programming")
textbook_input = gr.Textbox(label="Textbook", value="Computer Science for Kids")
chapter_unit_input = gr.Textbox(label="Chapter/Unit", value="Chapter 4: Algorithms")
standards_text_input = gr.Textbox(label="Standards (e.g., 3-5.IC.20)", value="3-5.IC.20")
learning_objectives_input = gr.Textbox(label="Learning Objectives (e.g., I can...)", lines=3, value="I can understand what an algorithm is.\nI can identify simple algorithms in daily life.")
strand_domain_input = gr.Textbox(label="Strand/Domain", value="Computational Thinking")
topic_input = gr.Textbox(label="Topic", value="Algorithms")
additional_ai_requirements = gr.Textbox(label="Additional AI Requirements for Lesson Content", lines=5,
placeholder="e.g., 'Include a creative group activity', 'Focus on problem-solving skills'.",
value="Generate a warm-up activity that involves movement. Provide a clear, step-by-step body of the lesson. Include a formative assessment at the end of the lesson.")
with gr.Column():
gr.Markdown("### Generated Outputs")
docx_output = gr.File(label="Generated Lesson Plan (DOCX)", file_types=[".docx"])
pptx_output = gr.File(label="Generated Presentation (PPTX)", file_types=[".pptx"])
#docx_output = gr.File(label="Generated Lesson Plan (DOCX)", type="binary")
#pptx_output = gr.File(label="Generated Presentation (PPTX)", type="binary", file_types=[".pptx"])
ai_raw_output_text = gr.Textbox(label="Raw AI Generated Lesson Content", lines=20, interactive=False)
ai_raw_pptx_output_text = gr.Textbox(label="Raw AI Generated Presentation Content", lines=20, interactive=False)
btn = gr.Button("Generate Lesson Plan")
generate_pptx_btn = gr.Button("Generate Presentation")
generate_from_pdf_btn = gr.Button("Generate Lesson Plan using the uploaded Lesson File")
# Event handlers
autofill_btn.click(
autofill_from_reference_pdf,
inputs=[pdf_file_input],
outputs=[
subject_input, grade_input, class_section_input, date_input, term_week_input,
school_input, teacher_name_input, total_students_input, sod_student_input,
gifted_student_input, lesson_title_input, textbook_input, chapter_unit_input,
standards_text_input, learning_objectives_input, strand_domain_input, topic_input,
extracted_details_textbox
]
)
btn.click(
create_document_from_inputs_and_ai,
inputs=[
subject_input, grade_input, class_section_input, date_input, term_week_input,
school_input, teacher_name_input, total_students_input, sod_student_input,
gifted_student_input, lesson_title_input, textbook_input, chapter_unit_input,
standards_text_input, learning_objectives_input, strand_domain_input, topic_input,
additional_ai_requirements
],
outputs=[docx_output, ai_raw_output_text]
)
generate_pptx_btn.click(
create_presentation_from_inputs_and_ai,
inputs=[
subject_input, grade_input, class_section_input, date_input, term_week_input,
school_input, teacher_name_input, total_students_input, sod_student_input,
gifted_student_input, lesson_title_input, textbook_input, chapter_unit_input,
standards_text_input, learning_objectives_input, strand_domain_input, topic_input,
additional_ai_requirements
],
outputs=[pptx_output, ai_raw_pptx_output_text]
)
# Add this event handler at the bottom of the Gradio interface
generate_from_pdf_btn.click(
generate_lesson_plan_from_pdf,
inputs=[pdf_file_input, additional_ai_requirements],
outputs=[docx_output, ai_raw_output_text]
)
demo.launch()