sunbal7 commited on
Commit
4ad4653
Β·
verified Β·
1 Parent(s): fdd5bb4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +450 -245
app.py CHANGED
@@ -7,17 +7,20 @@ import io
7
  import requests
8
  import re
9
  import os
 
 
10
 
11
  # --- Config ---
12
- DEFAULT_API_KEY = "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"
13
  API_URL = "https://openrouter.ai/api/v1/chat/completions"
14
  MODEL = "mistralai/mistral-7b-instruct"
15
 
16
  # Set page config
17
  st.set_page_config(
18
- page_title="πŸ”¬ AI Science Lab Assistant",
19
  layout="centered",
20
- page_icon="πŸ”¬"
 
21
  )
22
 
23
  # Custom CSS for styling
@@ -80,279 +83,481 @@ st.markdown("""
80
  padding: 15px;
81
  margin: 15px 0;
82
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  </style>
84
  """, unsafe_allow_html=True)
85
 
86
  # Header
87
- st.markdown('<p class="header">πŸ”¬ AI Science Lab Assistant</p>', unsafe_allow_html=True)
88
 
89
  # Introduction
90
  st.markdown("""
91
  <div style="text-align: center; margin-bottom: 30px;">
92
- <p style="font-size: 18px;">Transform your lab reports with AI-powered analysis! Get instant feedback on completeness,
93
- receive improvement suggestions, and ask questions about your scientific work.</p>
94
  </div>
95
  """, unsafe_allow_html=True)
96
 
97
  # API Key Setup
98
- st.markdown("---")
99
- st.markdown("### πŸ”‘ API Configuration")
100
- st.info("You need an API key from [OpenRouter](https://openrouter.ai/) to use this tool. Get a free key and paste it below.")
101
-
102
- api_key = st.text_input("Enter your OpenRouter API Key:", type="password",
103
- help="Get your API key from https://openrouter.ai/keys",
104
- value=os.getenv("OPENROUTER_API_KEY", DEFAULT_API_KEY))
105
-
106
- # Features in columns
107
- col1, col2, col3 = st.columns(3)
108
- with col1:
109
- st.markdown("""
110
- <div style="text-align: center;">
111
- <h4>πŸ” Comprehensive Analysis</h4>
112
- <p>Checks for all essential lab report sections</p>
113
- </div>
114
- """, unsafe_allow_html=True)
115
 
116
- with col2:
117
- st.markdown("""
118
- <div style="text-align: center;">
119
- <h4>πŸ’― Smart Scoring</h4>
120
- <p>Grades your report on completeness and structure</p>
121
- </div>
122
- """, unsafe_allow_html=True)
123
 
124
- with col3:
125
- st.markdown("""
126
- <div style="text-align: center;">
127
- <h4>πŸ“ˆ Improvement Tips</h4>
128
- <p>Personalized suggestions to enhance your report</p>
129
- </div>
130
- """, unsafe_allow_html=True)
131
-
132
- # Divider
133
- st.markdown("---")
134
-
135
- # --- File Upload ---
136
- st.markdown('<p class="subheader">πŸ“€ Upload Your Lab Report</p>', unsafe_allow_html=True)
137
- uploaded_file = st.file_uploader("Upload image (JPG, PNG) or PDF",
138
- type=["jpg", "jpeg", "png", "pdf"],
139
- label_visibility="collapsed")
140
-
141
- lab_text = ""
142
- if uploaded_file:
143
- file_bytes = uploaded_file.read()
144
- file_ext = uploaded_file.name.split(".")[-1].lower()
145
-
146
- if file_ext == "pdf":
147
- doc = fitz.open(stream=file_bytes, filetype="pdf")
148
- for page in doc:
149
- lab_text += page.get_text()
150
- else:
151
- image = Image.open(io.BytesIO(file_bytes))
152
- lab_text = pytesseract.image_to_string(image)
 
153
 
154
- # Allow text editing
155
- st.markdown('<p class="subheader">✍️ Extracted Text</p>', unsafe_allow_html=True)
156
- st.caption("Review and edit the extracted text if needed before analysis")
157
- lab_text = st.text_area("", lab_text, height=300, label_visibility="collapsed")
158
 
159
- # --- AI Evaluation ---
160
- if lab_text.strip() and api_key:
161
- # -- AI Evaluation Prompt --
162
- full_prompt = f"""You are a science teacher evaluating a student's lab report. Please provide a comprehensive analysis:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
- Lab Report:
165
- {lab_text}
 
166
 
167
- Evaluation Guidelines:
168
- 1. **Section Check**: Identify which of these sections are present and which are missing:
169
- - Title
170
- - Objective
171
- - Hypothesis
172
- - Materials
173
- - Procedure
174
- - Observations
175
- - Results
176
- - Conclusion
177
- - References
178
-
179
- 2. **Completeness Score**:
180
- - Assign a numerical score from 1-10 based on completeness
181
- - Justify the score based on missing sections and content quality
182
 
183
- 3. **Improvement Tips**:
184
- - For each missing section, explain why it's important
185
- - Provide specific suggestions for improvement (e.g., "Try writing a more detailed observation section by including quantitative data")
186
- - Highlight any sections that need more detail or clarity
187
-
188
- 4. **Structure Response**:
189
- - Start with: "### Missing Sections:"
190
- - Then: "### Completeness Score: X/10"
191
- - Then: "### Improvement Tips:"
192
- - Finally: "### Detailed Feedback:"
 
 
 
 
 
 
 
 
 
 
 
193
 
194
- Be concise but thorough in your analysis.
195
- """
196
-
197
- def query_ai(prompt, api_key):
198
- headers = {
199
- "Authorization": f"Bearer {api_key}",
200
- "Content-Type": "application/json"
201
- }
202
- payload = {
203
- "model": MODEL,
204
- "messages": [
205
- {"role": "system", "content": "You are a helpful science teacher providing detailed lab report feedback."},
206
- {"role": "user", "content": prompt}
207
- ]
208
- }
209
- try:
210
- response = requests.post(API_URL, headers=headers, json=payload, timeout=120)
211
- response.raise_for_status()
212
- return response.json()['choices'][0]['message']['content']
213
- except requests.exceptions.HTTPError as err:
214
- st.error(f"API Error: {err.response.status_code} - {err.response.text}")
215
- return None
216
- except Exception as e:
217
- st.error(f"Error connecting to AI service: {str(e)}")
218
- return None
219
-
220
- if st.button("πŸ§ͺ Analyze Report", use_container_width=True):
221
- with st.spinner("πŸ” Analyzing report with AI. This may take 20-30 seconds..."):
222
- result = query_ai(full_prompt, api_key)
223
-
224
- if result:
225
- st.success("βœ… Analysis Complete!")
226
- st.balloons()
227
-
228
- # Extract score using regex
229
- score_match = re.search(r"Completeness Score:\s*(\d+)/10", result, re.IGNORECASE)
230
- score = int(score_match.group(1)) if score_match else None
231
-
232
- # Display score in a card
233
- if score is not None:
234
- with st.container():
235
- st.markdown('<div class="score-card">', unsafe_allow_html=True)
236
-
237
- # Create columns for score visualization
238
- col1, col2 = st.columns([1, 3])
239
-
240
- with col1:
241
- st.markdown(f"<h2 style='text-align: center; color: #28b463;'>{score}/10</h2>",
242
- unsafe_allow_html=True)
243
- st.markdown("<h4 style='text-align: center;'>Completeness Score</h4>",
244
- unsafe_allow_html=True)
245
-
246
- with col2:
247
- # Create a color gradient based on score
248
- if score >= 8:
249
- color = "#28b463" # Green
250
- elif score >= 5:
251
- color = "#f39c12" # Orange
252
- else:
253
- color = "#e74c3c" # Red
254
-
255
- # Display progress bar with styling
256
- st.progress(score/10, text=f"{score*10}% complete")
257
- st.markdown(
258
- f"<style>"
259
- f".stProgress > div > div > div {{"
260
- f" background-color: {color} !important;"
261
- f" border-radius: 10px;"
262
- f"}}"
263
- f"</style>",
264
- unsafe_allow_html=True
265
- )
266
-
267
- st.markdown('</div>', unsafe_allow_html=True)
268
-
269
- # Display AI analysis with formatting
270
- st.markdown("## πŸ“ Analysis Results")
271
 
272
- # Split sections for better display
273
- sections = {
274
- "Missing Sections": None,
275
- "Improvement Tips": None,
276
- "Detailed Feedback": None
277
- }
278
 
279
- current_section = None
280
- for line in result.split('\n'):
281
- if "### Missing Sections:" in line:
282
- current_section = "Missing Sections"
283
- sections[current_section] = []
284
- elif "### Improvement Tips:" in line:
285
- current_section = "Improvement Tips"
286
- sections[current_section] = []
287
- elif "### Detailed Feedback:" in line:
288
- current_section = "Detailed Feedback"
289
- sections[current_section] = []
290
- elif current_section and line.strip():
291
- sections[current_section].append(line)
292
 
293
- # Display each section
294
- if sections["Missing Sections"]:
295
- st.markdown("### πŸ” Missing Sections")
296
- missing_text = '\n'.join(sections["Missing Sections"])
297
- st.markdown(f'<div class="highlight">{missing_text}</div>', unsafe_allow_html=True)
 
 
298
 
299
- if sections["Improvement Tips"]:
300
- st.markdown("### πŸ’‘ Improvement Tips")
301
- tips_text = '\n'.join(sections["Improvement Tips"])
302
- st.markdown(f'<div class="tip-box">{tips_text}</div>', unsafe_allow_html=True)
 
303
 
304
- if sections["Detailed Feedback"]:
305
- st.markdown("### πŸ“‹ Detailed Feedback")
306
- st.write('\n'.join(sections["Detailed Feedback"]))
307
 
308
- # Show full AI response in expander
309
- with st.expander("View Full AI Analysis"):
310
- st.markdown(result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
 
312
- # --- Question Answering Section ---
 
 
 
 
 
 
313
  st.markdown("---")
314
- st.markdown('<p class="subheader">❓ Ask About Your Report</p>', unsafe_allow_html=True)
315
 
316
- col1, col2 = st.columns([3, 1])
317
  with col1:
318
- user_question = st.text_input("Ask a question about your lab report",
319
- placeholder="e.g., How can I improve my hypothesis?")
320
- with col2:
321
- st.markdown("<div style='height: 28px;'></div>", unsafe_allow_html=True)
322
- ask_button = st.button("πŸ” Ask Question", use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
323
 
324
- if (ask_button or user_question) and user_question.strip():
325
- with st.spinner("Thinking..."):
326
- followup_prompt = f"""Lab Report:
327
- {lab_text}
 
 
 
 
 
328
 
329
- Question: {user_question}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
 
331
- Answer the question based on the lab report. If the question can't be answered from the report,
332
- suggest what information the student should add to answer it.
333
- """
334
- followup_response = query_ai(followup_prompt, api_key)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
 
336
- if followup_response:
337
- st.markdown("### πŸ’¬ AI Response")
338
- st.markdown(f'<div class="tip-box">{followup_response}</div>', unsafe_allow_html=True)
339
- elif lab_text.strip() and not api_key:
340
- st.error("⚠️ Please enter a valid OpenRouter API key to analyze your report")
341
- else:
342
- # Show sample report if no file uploaded
343
- st.markdown("---")
344
- st.markdown('<p class="subheader">πŸ“ Sample Lab Report</p>', unsafe_allow_html=True)
345
- st.markdown("""
346
- **Title:** Effect of Temperature on Enzyme Activity
347
- **Objective:** To investigate how temperature affects catalase enzyme activity
348
- **Hypothesis:** Enzyme activity will increase with temperature up to 37Β°C, then decrease
349
- **Materials:** Test tubes, hydrogen peroxide, liver extract, thermometer
350
- **Procedure:**
351
- 1. Prepare test tubes at 5 different temperatures
352
- 2. Add equal amounts of hydrogen peroxide and liver extract
353
- 3. Measure oxygen production
354
- **Observations:** More bubbles at 37Β°C compared to lower or higher temperatures
355
- **Conclusion:** Enzyme activity peaks at body temperature
356
- """)
357
-
358
- st.info("πŸ‘† Upload your own lab report to get a personalized analysis!")
 
7
  import requests
8
  import re
9
  import os
10
+ from fpdf import FPDF
11
+ from datetime import datetime
12
 
13
  # --- Config ---
14
+ DEFAULT_API_KEY = "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"
15
  API_URL = "https://openrouter.ai/api/v1/chat/completions"
16
  MODEL = "mistralai/mistral-7b-instruct"
17
 
18
  # Set page config
19
  st.set_page_config(
20
+ page_title="πŸ”¬ Science Lab Assistant",
21
  layout="centered",
22
+ page_icon="πŸ”¬",
23
+ initial_sidebar_state="expanded"
24
  )
25
 
26
  # Custom CSS for styling
 
83
  padding: 15px;
84
  margin: 15px 0;
85
  }
86
+ .experiment-card {
87
+ background: linear-gradient(135deg, #f0f7ff, #e1effe);
88
+ border-radius: 15px;
89
+ padding: 20px;
90
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
91
+ margin-bottom: 20px;
92
+ transition: all 0.3s;
93
+ }
94
+ .experiment-card:hover {
95
+ transform: translateY(-5px);
96
+ box-shadow: 0 6px 12px rgba(0,0,0,0.15);
97
+ }
98
+ .concept-box {
99
+ background-color: #ebf5fb;
100
+ border-left: 5px solid #3498db;
101
+ padding: 15px;
102
+ margin: 15px 0;
103
+ border-radius: 0 8px 8px 0;
104
+ }
105
+ [data-testid="stSidebar"] {
106
+ background: linear-gradient(180deg, #e8f8f5, #d1f2eb) !important;
107
+ }
108
  </style>
109
  """, unsafe_allow_html=True)
110
 
111
  # Header
112
+ st.markdown('<p class="header">πŸ”¬ Science Lab Assistant</p>', unsafe_allow_html=True)
113
 
114
  # Introduction
115
  st.markdown("""
116
  <div style="text-align: center; margin-bottom: 30px;">
117
+ <p style="font-size: 18px;">Your all-in-one science companion! Design experiments, generate reports,
118
+ and get AI-powered feedback on your lab work.</p>
119
  </div>
120
  """, unsafe_allow_html=True)
121
 
122
  # API Key Setup
123
+ with st.sidebar:
124
+ st.markdown("## πŸ”‘ API Configuration")
125
+ st.info("You need an API key from [OpenRouter](https://openrouter.ai/) to use this tool. Get a free key and paste it below.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
+ api_key = st.text_input("Enter your OpenRouter API Key:", type="password",
128
+ help="Get your API key from https://openrouter.ai/keys",
129
+ value=os.getenv("OPENROUTER_API_KEY", DEFAULT_API_KEY))
 
 
 
 
130
 
131
+ st.markdown("---")
132
+ st.markdown("### πŸ§ͺ Experiment Templates")
133
+ st.caption("Quickly start with these pre-defined experiments:")
134
+
135
+ # Experiment templates
136
+ experiments = {
137
+ "Vinegar + Baking Soda": {
138
+ "hypothesis": "Mixing vinegar and baking soda will produce bubbles due to a chemical reaction.",
139
+ "concept": "Acid-base reaction producing carbon dioxide."
140
+ },
141
+ "Floating Egg": {
142
+ "hypothesis": "An egg will float in salt water but sink in plain water.",
143
+ "concept": "Density difference between saltwater and freshwater."
144
+ },
145
+ "Lemon Battery": {
146
+ "hypothesis": "A lemon can produce electricity to power a small LED.",
147
+ "concept": "Chemical energy conversion to electrical energy."
148
+ }
149
+ }
150
+
151
+ selected_exp = st.selectbox("Choose an experiment template:",
152
+ list(experiments.keys()) + ["Custom Experiment"])
153
+
154
+ st.markdown("---")
155
+ st.markdown("### πŸ“˜ Science Glossary Helper")
156
+ term = st.text_input("Enter a science term (e.g., osmosis, catalyst)")
157
+ if term:
158
+ with st.spinner("Looking up term..."):
159
+ ai_response = st.session_state.get('query_ai', lambda p: f"Explain '{term}'")(f"Explain the term '{term}' in simple words for a student.")
160
+ st.markdown(f"<div class='concept-box'>{ai_response}</div>", unsafe_allow_html=True)
161
 
162
+ # Navigation
163
+ app_mode = st.radio("Choose Mode:", ["πŸ§ͺ Experiment Assistant", "πŸ“ Lab Report Analyzer"],
164
+ horizontal=True, label_visibility="collapsed")
 
165
 
166
+ # AI Query Function
167
+ def query_ai(prompt, api_key):
168
+ if not api_key:
169
+ st.error("⚠️ Please enter a valid API key")
170
+ return None
171
+
172
+ headers = {
173
+ "Authorization": f"Bearer {api_key}",
174
+ "Content-Type": "application/json"
175
+ }
176
+ payload = {
177
+ "model": MODEL,
178
+ "messages": [
179
+ {"role": "system", "content": "You are a helpful science teacher providing detailed explanations."},
180
+ {"role": "user", "content": prompt}
181
+ ],
182
+ "temperature": 0.7
183
+ }
184
+ try:
185
+ response = requests.post(API_URL, headers=headers, json=payload, timeout=120)
186
+ response.raise_for_status()
187
+ return response.json()['choices'][0]['message']['content']
188
+ except requests.exceptions.HTTPError as err:
189
+ st.error(f"API Error: {err.response.status_code} - {err.response.text}")
190
+ return None
191
+ except Exception as e:
192
+ st.error(f"Error connecting to AI service: {str(e)}")
193
+ return None
194
 
195
+ # Store query_ai in session state for glossary helper
196
+ if 'query_ai' not in st.session_state:
197
+ st.session_state.query_ai = query_ai
198
 
199
+ # --- Experiment Assistant Section ---
200
+ if app_mode == "πŸ§ͺ Experiment Assistant":
201
+ st.markdown('<p class="subheader">πŸ” Design Your Experiment</p>', unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
202
 
203
+ with st.form("experiment_form"):
204
+ # Pre-fill if template selected
205
+ if selected_exp != "Custom Experiment" and selected_exp in experiments:
206
+ default_hypo = experiments[selected_exp]["hypothesis"]
207
+ concept = experiments[selected_exp]["concept"]
208
+ exp_name = selected_exp
209
+ else:
210
+ default_hypo = ""
211
+ concept = ""
212
+ exp_name = st.text_input("Experiment Name", placeholder="e.g., Effect of Temperature on Enzyme Activity")
213
+
214
+ hypo = st.text_area("Your Hypothesis", value=default_hypo,
215
+ placeholder="What do you predict will happen?")
216
+
217
+ materials = st.text_area("Materials Needed",
218
+ placeholder="List all materials needed for this experiment")
219
+
220
+ procedure = st.text_area("Procedure Steps",
221
+ placeholder="Step-by-step instructions for conducting the experiment")
222
+
223
+ submit = st.form_submit_button("πŸ” Generate Experiment Guide", use_container_width=True)
224
 
225
+ if submit:
226
+ if not exp_name or not hypo:
227
+ st.warning("Please provide at least an experiment name and hypothesis")
228
+ st.stop()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
+ with st.spinner("Designing your experiment guide..."):
231
+ prompt = f"""
232
+ Create a comprehensive guide for a science experiment with the following details:
 
 
 
233
 
234
+ Experiment Name: {exp_name}
235
+ Hypothesis: {hypo}
236
+ Materials: {materials if materials else 'Not specified'}
237
+ Procedure: {procedure if procedure else 'Not specified'}
 
 
 
 
 
 
 
 
 
238
 
239
+ Please provide:
240
+ 1. A clear explanation of the scientific concept behind the experiment
241
+ 2. Step-by-step instructions for conducting the experiment
242
+ 3. Safety precautions
243
+ 4. Expected results and why they're expected
244
+ 5. How to interpret the results
245
+ """
246
 
247
+ explanation = query_ai(prompt, api_key)
248
+
249
+ if explanation:
250
+ st.success("βœ… Experiment Guide Generated!")
251
+ st.balloons()
252
 
253
+ # Display explanation
254
+ st.markdown("### πŸ§ͺ Experiment Guide")
255
+ st.markdown(f"<div class='tip-box'>{explanation}</div>", unsafe_allow_html=True)
256
 
257
+ # Generate PDF report
258
+ def generate_pdf_report(exp_name, hypo, explanation, materials, procedure):
259
+ pdf = FPDF()
260
+ pdf.add_page()
261
+ pdf.set_font("Arial", size=12)
262
+
263
+ # Title
264
+ pdf.set_font("Arial", 'B', 16)
265
+ pdf.cell(200, 10, txt="Science Experiment Guide", ln=True, align='C')
266
+ pdf.ln(15)
267
+
268
+ # Experiment details
269
+ pdf.set_font("Arial", 'B', 14)
270
+ pdf.cell(0, 10, txt=f"Experiment: {exp_name}", ln=True)
271
+ pdf.ln(5)
272
+
273
+ pdf.set_font("Arial", 'B', 12)
274
+ pdf.cell(0, 10, txt="Hypothesis:", ln=True)
275
+ pdf.set_font("Arial", '', 12)
276
+ pdf.multi_cell(0, 8, txt=hypo)
277
+ pdf.ln(5)
278
+
279
+ if materials:
280
+ pdf.set_font("Arial", 'B', 12)
281
+ pdf.cell(0, 10, txt="Materials:", ln=True)
282
+ pdf.set_font("Arial", '', 12)
283
+ pdf.multi_cell(0, 8, txt=materials)
284
+ pdf.ln(5)
285
+
286
+ if procedure:
287
+ pdf.set_font("Arial", 'B', 12)
288
+ pdf.cell(0, 10, txt="Procedure:", ln=True)
289
+ pdf.set_font("Arial", '', 12)
290
+ pdf.multi_cell(0, 8, txt=procedure)
291
+ pdf.ln(10)
292
+
293
+ pdf.set_font("Arial", 'B', 12)
294
+ pdf.cell(0, 10, txt="Experiment Guide:", ln=True)
295
+ pdf.set_font("Arial", '', 12)
296
+ pdf.multi_cell(0, 8, txt=explanation)
297
+
298
+ filename = f"experiment_guide_{datetime.now().strftime('%Y%m%d%H%M%S')}.pdf"
299
+ pdf.output(filename)
300
+ return filename
301
 
302
+ pdf_file = generate_pdf_report(exp_name, hypo, explanation, materials, procedure)
303
+ with open(pdf_file, "rb") as file:
304
+ st.download_button("πŸ“„ Download Experiment Guide (PDF)", file,
305
+ file_name=f"{exp_name}_guide.pdf",
306
+ use_container_width=True)
307
+
308
+ # Experiment examples
309
  st.markdown("---")
310
+ st.markdown('<p class="subheader">πŸ”¬ Popular Science Experiments</p>', unsafe_allow_html=True)
311
 
312
+ col1, col2 = st.columns(2)
313
  with col1:
314
+ with st.container():
315
+ st.markdown('<div class="experiment-card">', unsafe_allow_html=True)
316
+ st.markdown("#### 🧫 Vinegar + Baking Soda")
317
+ st.markdown("**Hypothesis:** Mixing vinegar and baking soda will produce bubbles due to a chemical reaction.")
318
+ st.markdown("**Concept:** Acid-base reaction producing carbon dioxide.")
319
+ if st.button("Try This Experiment", key="vinegar", use_container_width=True):
320
+ st.session_state.selected_exp = "Vinegar + Baking Soda"
321
+ st.markdown('</div>', unsafe_allow_html=True)
322
+
323
+ with st.container():
324
+ st.markdown('<div class="experiment-card">', unsafe_allow_html=True)
325
+ st.markdown("#### πŸ₯š Floating Egg")
326
+ st.markdown("**Hypothesis:** An egg will float in salt water but sink in plain water.")
327
+ st.markdown("**Concept:** Density difference between saltwater and freshwater.")
328
+ if st.button("Try This Experiment", key="egg", use_container_width=True):
329
+ st.session_state.selected_exp = "Floating Egg"
330
+ st.markdown('</div>', unsafe_allow_html=True)
331
 
332
+ with col2:
333
+ with st.container():
334
+ st.markdown('<div class="experiment-card">', unsafe_allow_html=True)
335
+ st.markdown("#### πŸ‹ Lemon Battery")
336
+ st.markdown("**Hypothesis:** A lemon can produce electricity to power a small LED.")
337
+ st.markdown("**Concept:** Chemical energy conversion to electrical energy.")
338
+ if st.button("Try This Experiment", key="lemon", use_container_width=True):
339
+ st.session_state.selected_exp = "Lemon Battery"
340
+ st.markdown('</div>', unsafe_allow_html=True)
341
 
342
+ with st.container():
343
+ st.markdown('<div class="experiment-card">', unsafe_allow_html=True)
344
+ st.markdown("#### 🌈 Rainbow in a Glass")
345
+ st.markdown("**Hypothesis:** Different sugar solutions can form colorful layers in a glass.")
346
+ st.markdown("**Concept:** Density gradient formation.")
347
+ if st.button("Try This Experiment", key="rainbow", use_container_width=True):
348
+ st.session_state.selected_exp = "Custom Experiment"
349
+ st.session_state.custom_exp = "Rainbow in a Glass"
350
+ st.session_state.custom_hypo = "Different sugar solutions will form distinct layers based on their density."
351
+ st.markdown('</div>', unsafe_allow_html=True)
352
+
353
+ # --- Lab Report Analyzer Section ---
354
+ else:
355
+ # --- File Upload ---
356
+ st.markdown('<p class="subheader">πŸ“€ Upload Your Lab Report</p>', unsafe_allow_html=True)
357
+ uploaded_file = st.file_uploader("Upload image (JPG, PNG) or PDF",
358
+ type=["jpg", "jpeg", "png", "pdf"],
359
+ label_visibility="collapsed")
360
+
361
+ lab_text = ""
362
+ if uploaded_file:
363
+ file_bytes = uploaded_file.read()
364
+ file_ext = uploaded_file.name.split(".")[-1].lower()
365
+
366
+ if file_ext == "pdf":
367
+ doc = fitz.open(stream=file_bytes, filetype="pdf")
368
+ for page in doc:
369
+ lab_text += page.get_text()
370
+ else:
371
+ image = Image.open(io.BytesIO(file_bytes))
372
+ lab_text = pytesseract.image_to_string(image)
373
+
374
+ # Allow text editing
375
+ st.markdown('<p class="subheader">✍️ Extracted Text</p>', unsafe_allow_html=True)
376
+ st.caption("Review and edit the extracted text if needed before analysis")
377
+ lab_text = st.text_area("", lab_text, height=300, label_visibility="collapsed")
378
+
379
+ # --- AI Evaluation ---
380
+ if lab_text.strip() and api_key:
381
+ # -- AI Evaluation Prompt --
382
+ full_prompt = f"""You are a science teacher evaluating a student's lab report. Please provide a comprehensive analysis:
383
+
384
+ Lab Report:
385
+ {lab_text}
386
+
387
+ Evaluation Guidelines:
388
+ 1. **Section Check**: Identify which of these sections are present and which are missing:
389
+ - Title
390
+ - Objective
391
+ - Hypothesis
392
+ - Materials
393
+ - Procedure
394
+ - Observations
395
+ - Results
396
+ - Conclusion
397
+ - References
398
+
399
+ 2. **Completeness Score**:
400
+ - Assign a numerical score from 1-10 based on completeness
401
+ - Justify the score based on missing sections and content quality
402
+
403
+ 3. **Improvement Tips**:
404
+ - For each missing section, explain why it's important
405
+ - Provide specific suggestions for improvement (e.g., "Try writing a more detailed observation section by including quantitative data")
406
+ - Highlight any sections that need more detail or clarity
407
+
408
+ 4. **Structure Response**:
409
+ - Start with: "### Missing Sections:"
410
+ - Then: "### Completeness Score: X/10"
411
+ - Then: "### Improvement Tips:"
412
+ - Finally: "### Detailed Feedback:"
413
+
414
+ Be concise but thorough in your analysis.
415
+ """
416
+
417
+ if st.button("πŸ§ͺ Analyze Report", use_container_width=True):
418
+ with st.spinner("πŸ” Analyzing report with AI. This may take 20-30 seconds..."):
419
+ result = query_ai(full_prompt, api_key)
420
+
421
+ if result:
422
+ st.success("βœ… Analysis Complete!")
423
+ st.balloons()
424
+
425
+ # Extract score using regex
426
+ score_match = re.search(r"Completeness Score:\s*(\d+)/10", result, re.IGNORECASE)
427
+ score = int(score_match.group(1)) if score_match else None
428
+
429
+ # Display score in a card
430
+ if score is not None:
431
+ with st.container():
432
+ st.markdown('<div class="score-card">', unsafe_allow_html=True)
433
+
434
+ # Create columns for score visualization
435
+ col1, col2 = st.columns([1, 3])
436
+
437
+ with col1:
438
+ st.markdown(f"<h2 style='text-align: center; color: #28b463;'>{score}/10</h2>",
439
+ unsafe_allow_html=True)
440
+ st.markdown("<h4 style='text-align: center;'>Completeness Score</h4>",
441
+ unsafe_allow_html=True)
442
+
443
+ with col2:
444
+ # Create a color gradient based on score
445
+ if score >= 8:
446
+ color = "#28b463" # Green
447
+ elif score >= 5:
448
+ color = "#f39c12" # Orange
449
+ else:
450
+ color = "#e74c3c" # Red
451
+
452
+ # Display progress bar with styling
453
+ st.progress(score/10, text=f"{score*10}% complete")
454
+ st.markdown(
455
+ f"<style>"
456
+ f".stProgress > div > div > div {{"
457
+ f" background-color: {color} !important;"
458
+ f" border-radius: 10px;"
459
+ f"}}"
460
+ f"</style>",
461
+ unsafe_allow_html=True
462
+ )
463
+
464
+ st.markdown('</div>', unsafe_allow_html=True)
465
+
466
+ # Display AI analysis with formatting
467
+ st.markdown("## πŸ“ Analysis Results")
468
+
469
+ # Split sections for better display
470
+ sections = {
471
+ "Missing Sections": None,
472
+ "Improvement Tips": None,
473
+ "Detailed Feedback": None
474
+ }
475
+
476
+ current_section = None
477
+ for line in result.split('\n'):
478
+ if "### Missing Sections:" in line:
479
+ current_section = "Missing Sections"
480
+ sections[current_section] = []
481
+ elif "### Improvement Tips:" in line:
482
+ current_section = "Improvement Tips"
483
+ sections[current_section] = []
484
+ elif "### Detailed Feedback:" in line:
485
+ current_section = "Detailed Feedback"
486
+ sections[current_section] = []
487
+ elif current_section and line.strip():
488
+ sections[current_section].append(line)
489
+
490
+ # Display each section
491
+ if sections["Missing Sections"]:
492
+ st.markdown("### πŸ” Missing Sections")
493
+ missing_text = '\n'.join(sections["Missing Sections"])
494
+ st.markdown(f'<div class="highlight">{missing_text}</div>', unsafe_allow_html=True)
495
+
496
+ if sections["Improvement Tips"]:
497
+ st.markdown("### πŸ’‘ Improvement Tips")
498
+ tips_text = '\n'.join(sections["Improvement Tips"])
499
+ st.markdown(f'<div class="tip-box">{tips_text}</div>', unsafe_allow_html=True)
500
+
501
+ if sections["Detailed Feedback"]:
502
+ st.markdown("### πŸ“‹ Detailed Feedback")
503
+ st.write('\n'.join(sections["Detailed Feedback"]))
504
+
505
+ # Show full AI response in expander
506
+ with st.expander("View Full AI Analysis"):
507
+ st.markdown(result)
508
+
509
+ # --- Question Answering Section ---
510
+ st.markdown("---")
511
+ st.markdown('<p class="subheader">❓ Ask About Your Report</p>', unsafe_allow_html=True)
512
+
513
+ col1, col2 = st.columns([3, 1])
514
+ with col1:
515
+ user_question = st.text_input("Ask a question about your lab report",
516
+ placeholder="e.g., How can I improve my hypothesis?")
517
+ with col2:
518
+ st.markdown("<div style='height: 28px;'></div>", unsafe_allow_html=True)
519
+ ask_button = st.button("πŸ” Ask Question", use_container_width=True)
520
+
521
+ if (ask_button or user_question) and user_question.strip():
522
+ with st.spinner("Thinking..."):
523
+ followup_prompt = f"""Lab Report:
524
+ {lab_text}
525
+
526
+ Question: {user_question}
527
+
528
+ Answer the question based on the lab report. If the question can't be answered from the report,
529
+ suggest what information the student should add to answer it.
530
+ """
531
+ followup_response = query_ai(followup_prompt, api_key)
532
 
533
+ if followup_response:
534
+ st.markdown("### πŸ’¬ AI Response")
535
+ st.markdown(f'<div class="tip-box">{followup_response}</div>', unsafe_allow_html=True)
536
+ elif lab_text.strip() and not api_key:
537
+ st.error("⚠️ Please enter a valid OpenRouter API key to analyze your report")
538
+ else:
539
+ # Show sample report if no file uploaded
540
+ st.markdown("---")
541
+ st.markdown('<p class="subheader">πŸ“ Sample Lab Report</p>', unsafe_allow_html=True)
542
+ st.markdown("""
543
+ **Title:** Effect of Temperature on Enzyme Activity
544
+ **Objective:** To investigate how temperature affects catalase enzyme activity
545
+ **Hypothesis:** Enzyme activity will increase with temperature up to 37Β°C, then decrease
546
+ **Materials:** Test tubes, hydrogen peroxide, liver extract, thermometer
547
+ **Procedure:**
548
+ 1. Prepare test tubes at 5 different temperatures
549
+ 2. Add equal amounts of hydrogen peroxide and liver extract
550
+ 3. Measure oxygen production
551
+ **Observations:** More bubbles at 37Β°C compared to lower or higher temperatures
552
+ **Conclusion:** Enzyme activity peaks at body temperature
553
+ """)
554
 
555
+ st.info("πŸ‘† Upload your own lab report to get a personalized analysis!")
556
+
557
+ # --- Feedback Section ---
558
+ st.markdown("---")
559
+ with st.expander("πŸ’¬ Send Feedback"):
560
+ st.markdown("We'd love to hear your thoughts to improve this tool!")
561
+ feedback = st.text_area("What can we do better?")
562
+ if st.button("Submit Feedback", use_container_width=True):
563
+ st.success("Thank you for your feedback! We'll use it to improve the Science Lab Assistant.")