jeremierostan commited on
Commit
8f44461
Β·
verified Β·
1 Parent(s): a591556

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +219 -0
app.py ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import PyPDF2
3
+ from pptx import Presentation
4
+ from PIL import Image
5
+ import io
6
+ from google import genai
7
+ from jinja2 import Template
8
+ import fitz # PyMuPDF
9
+ import os
10
+ import logging
11
+ import re
12
+ import time
13
+
14
+ # Set up logging
15
+ logging.basicConfig(level=logging.INFO)
16
+
17
+ # Function to extract content from PDF
18
+ def extract_content_from_pdf(file_path):
19
+ try:
20
+ text = ""
21
+ images = []
22
+ doc = fitz.open(file_path)
23
+ for page in doc:
24
+ text += page.get_text() + "\n"
25
+ for img in page.get_images():
26
+ xref = img[0]
27
+ base_image = doc.extract_image(xref)
28
+ image_bytes = base_image["image"]
29
+ image = Image.open(io.BytesIO(image_bytes))
30
+ images.append(image)
31
+ return text, images
32
+ except Exception as e:
33
+ logging.error(f"Error extracting content from PDF: {e}")
34
+ return "", []
35
+
36
+ # Function to extract content from PPTX
37
+ def extract_content_from_pptx(file_path):
38
+ try:
39
+ text = ""
40
+ images = []
41
+ prs = Presentation(file_path)
42
+ for slide in prs.slides:
43
+ for shape in slide.shapes:
44
+ if hasattr(shape, 'text'):
45
+ text += shape.text + "\n"
46
+ if shape.shape_type == 13: # Picture
47
+ image = shape.image
48
+ image_bytes = image.blob
49
+ image = Image.open(io.BytesIO(image_bytes))
50
+ images.append(image)
51
+ return text, images
52
+ except Exception as e:
53
+ logging.error(f"Error extracting content from PPTX: {e}")
54
+ return "", []
55
+
56
+ # Function to process file
57
+ def process_file(file_path):
58
+ if file_path is None:
59
+ return "No file uploaded", []
60
+
61
+ try:
62
+ if file_path.lower().endswith('.pdf'):
63
+ return extract_content_from_pdf(file_path)
64
+ elif file_path.lower().endswith('.pptx'):
65
+ return extract_content_from_pptx(file_path)
66
+ else:
67
+ return "Unsupported file format", []
68
+ except Exception as e:
69
+ logging.error(f"Error processing file: {e}")
70
+ return f"An error occurred while processing the file: {str(e)}", []
71
+
72
+ # Function to clean response
73
+ def clean_response(response_text):
74
+ # Remove code block markers if present
75
+ cleaned = re.sub(r'```python|```', '', response_text).strip()
76
+ # Handle newlines and indentation
77
+ cleaned = re.sub(r'\n\s*\n', '\n\n', cleaned)
78
+ return cleaned
79
+
80
+ # Function to understand text and images using Gemini V2 API
81
+ def understand_content(api_key, text, images, progress=gr.Progress()):
82
+ try:
83
+ # Initialize the Gemini client
84
+ client = genai.Client(api_key=api_key)
85
+
86
+ progress(0.3, desc="Preparing content...")
87
+ # Prepare content for Gemini
88
+ content = [text]
89
+ for image in images[:10]: # Limit to 10 images
90
+ content.append(image)
91
+
92
+ progress(0.5, desc="Generating unit plan...")
93
+ # Generate response from Gemini
94
+ prompt = Template("""
95
+ You are an expert instructional designer.
96
+ Below are materials shared by a teacher. Your role is to reverse-engineer the unit planner for this content.
97
+ To do so:
98
+ 1) Read the content carefully
99
+ 2) Create a unit planner for this content that follows this exact structure:
100
+ {{ unit_plan_structure }}
101
+ """).render(unit_plan_structure="""
102
+ # Standards
103
+ # Transfer Goal
104
+ # Essential Questions
105
+ # Enduring Understandings
106
+ # Students will know
107
+ # Students will be able to
108
+ # Formative Assessments
109
+ # Summative Assessments
110
+ # Scope and Sequence
111
+ # Unit Overview
112
+ # Potential barriers
113
+ # Connections
114
+ ## ISP Core Values
115
+ ## IB Theory of Knowledge
116
+ ## IB Approaches to Learning
117
+ # Authentic Assessment
118
+ """)
119
+
120
+ response = client.models.generate_content(
121
+ model="gemini-2.0-flash-exp",
122
+ contents=prompt + text
123
+ )
124
+
125
+ progress(0.8, desc="Finalizing output...")
126
+ # Log the raw response for debugging
127
+ response_text = response.text
128
+ logging.info(f"Raw response from Gemini: {response_text}")
129
+
130
+ # Clean the response
131
+ cleaned_response = clean_response(response_text)
132
+ logging.info(f"Cleaned response: {cleaned_response}")
133
+
134
+ progress(1.0, desc="Complete!")
135
+ return cleaned_response
136
+
137
+ except Exception as e:
138
+ logging.error(f"Error in content understanding: {e}")
139
+ return f"Error in processing the content: {str(e)}. Please check your API key and try again."
140
+
141
+ # Function to reverse engineer unit plan
142
+ def generate_elt_plan(api_key, file, progress=gr.Progress()):
143
+ if not api_key:
144
+ return "Please enter your Gemini API key", None
145
+
146
+ try:
147
+ progress(0.1, desc="Starting file processing...")
148
+ logging.info(f"Processing file: {file.name}")
149
+ content, images = process_file(file.name)
150
+ if isinstance(content, str) and content.startswith("An error occurred"):
151
+ return content, None
152
+ logging.info(f"Extracted content length: {len(content)}, Number of images: {len(images)}")
153
+ progress(0.2, desc="Content extracted, generating plan...")
154
+ elt_plan = understand_content(api_key, content, images, progress)
155
+
156
+ # Create a downloadable text file
157
+ timestamp = time.strftime("%Y%m%d-%H%M%S")
158
+ filename = f"unit_plan_{timestamp}.txt"
159
+ with open(filename, "w", encoding="utf-8") as f:
160
+ f.write(elt_plan)
161
+
162
+ return elt_plan, filename
163
+ except Exception as e:
164
+ logging.error(f"Error in generate_elt_plan: {e}")
165
+ return f"An error occurred: {str(e)}", None
166
+
167
+ # Set up Gradio Blocks
168
+ with gr.Blocks() as demo:
169
+ gr.Markdown("# πŸ”„ Reverse Unit Planner")
170
+
171
+ with gr.Row():
172
+ with gr.Column():
173
+ api_key = gr.Textbox(label="Enter your Gemini API key", type="password")
174
+ file_input = gr.File(label="Upload PPTX or PDF")
175
+ submit_btn = gr.Button("Reverse engineer a unit plan", variant="primary")
176
+
177
+ with gr.Column():
178
+ output = gr.Markdown(label="Draft Unit Planner")
179
+ copy_btn = gr.Button("πŸ“‹ Copy to Clipboard")
180
+ download_btn = gr.File(label="Download Unit Plan")
181
+
182
+ # Handle the main processing
183
+ result = submit_btn.click(
184
+ generate_elt_plan,
185
+ inputs=[api_key, file_input],
186
+ outputs=[output, download_btn]
187
+ )
188
+
189
+ # Add copy to clipboard functionality
190
+ copy_btn.click(
191
+ fn=None,
192
+ inputs=output,
193
+ outputs=None,
194
+ js="""
195
+ async (text) => {
196
+ await navigator.clipboard.writeText(text);
197
+ await new Promise(resolve => {
198
+ const notification = document.createElement('div');
199
+ notification.textContent = 'Copied to clipboard!';
200
+ notification.style.position = 'fixed';
201
+ notification.style.bottom = '20px';
202
+ notification.style.left = '50%';
203
+ notification.style.transform = 'translateX(-50%)';
204
+ notification.style.backgroundColor = '#4CAF50';
205
+ notification.style.color = 'white';
206
+ notification.style.padding = '10px 20px';
207
+ notification.style.borderRadius = '5px';
208
+ notification.style.zIndex = '1000';
209
+ document.body.appendChild(notification);
210
+ setTimeout(() => {
211
+ notification.remove();
212
+ resolve();
213
+ }, 2000);
214
+ });
215
+ }
216
+ """)
217
+
218
+ # Launch the app
219
+ demo.launch()