sunbal7 commited on
Commit
74bd5a5
Β·
verified Β·
1 Parent(s): a99cc0d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +369 -32
app.py CHANGED
@@ -1,22 +1,32 @@
1
  # app.py
2
  import streamlit as st
 
 
3
  import fitz # PyMuPDF
4
  import io
5
  import requests
6
  import re
7
- import os
8
  from fpdf import FPDF
9
  from datetime import datetime
10
- from PIL import Image
11
- import base64
12
- import json
13
 
14
  # --- Config ---
15
  API_URL = "https://openrouter.ai/api/v1/chat/completions"
 
 
16
  MODEL = "mistralai/mistral-7b-instruct"
17
 
18
- # Retrieve API key from environment variable (set in Hugging Face secrets)
19
- OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
 
 
 
 
 
 
 
 
20
 
21
  # Set page config
22
  st.set_page_config(
@@ -29,7 +39,92 @@ st.set_page_config(
29
  # Custom CSS for styling
30
  st.markdown("""
31
  <style>
32
- /* (Keep all your existing CSS styles here) */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  </style>
34
  """, unsafe_allow_html=True)
35
 
@@ -44,22 +139,6 @@ st.markdown("""
44
  </div>
45
  """, unsafe_allow_html=True)
46
 
47
- # API Key Check
48
- if not OPENROUTER_API_KEY:
49
- st.error("""
50
- **API Key Not Configured!**
51
-
52
- Please add your OpenRouter API key to Hugging Face Spaces secrets:
53
- 1. Go to your Space settings
54
- 2. Select "Variables and secrets"
55
- 3. Add a secret named: `OPENROUTER_API_KEY`
56
- 4. Set its value to your actual API key
57
- 5. Redeploy the space
58
-
59
- Without this key, the AI features won't work.
60
- """)
61
- st.stop()
62
-
63
  # Experiment templates
64
  experiments = {
65
  "Vinegar + Baking Soda": {
@@ -79,7 +158,7 @@ experiments = {
79
  # AI Query Function
80
  def query_ai(prompt):
81
  headers = {
82
- "Authorization": f"Bearer {OPENROUTER_API_KEY}",
83
  "Content-Type": "application/json"
84
  }
85
  payload = {
@@ -121,18 +200,169 @@ with st.sidebar:
121
  ai_response = query_ai(f"Explain the term '{term}' in simple words for a student.")
122
  if ai_response:
123
  st.markdown(f"<div class='concept-box'>{ai_response}</div>", unsafe_allow_html=True)
 
 
124
 
125
  # --- Experiment Assistant Section ---
126
  if app_mode == "πŸ§ͺ Experiment Assistant":
127
- # (Keep all your experiment assistant code here unchanged)
128
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
130
  # --- Lab Report Analyzer Section ---
131
  else:
132
  # --- File Upload ---
133
  st.markdown('<p class="subheader">πŸ“€ Upload Your Lab Report</p>', unsafe_allow_html=True)
134
- uploaded_file = st.file_uploader("Upload PDF only (image support coming soon)",
135
- type=["pdf"],
136
  label_visibility="collapsed")
137
 
138
  lab_text = ""
@@ -149,8 +379,34 @@ else:
149
  except Exception as e:
150
  st.error(f"Error reading PDF: {str(e)}")
151
  else:
152
- st.warning("Image upload is temporarily disabled. Please upload PDF files only.")
153
- st.info("Tip: Convert images to PDF using free online tools")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
  # Allow text editing
156
  if lab_text:
@@ -204,8 +460,89 @@ else:
204
  st.success("βœ… Analysis Complete!")
205
  st.balloons()
206
 
207
- # (Keep all your analysis result display code here unchanged)
208
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
  # --- Question Answering Section ---
211
  st.markdown("---")
 
1
  # app.py
2
  import streamlit as st
3
+ import pytesseract
4
+ from PIL import Image
5
  import fitz # PyMuPDF
6
  import io
7
  import requests
8
  import re
9
+ import numpy as np
10
  from fpdf import FPDF
11
  from datetime import datetime
12
+ import os
 
 
13
 
14
  # --- Config ---
15
  API_URL = "https://openrouter.ai/api/v1/chat/completions"
16
+ # Retrieve API key from environment variable
17
+ API_KEY = os.getenv("OPENROUTER_API_KEY")
18
  MODEL = "mistralai/mistral-7b-instruct"
19
 
20
+ # Set Tesseract path for different environments
21
+ try:
22
+ # For Windows
23
+ if os.name == 'nt':
24
+ pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
25
+ # For Linux (Streamlit Sharing)
26
+ elif 'tesseract' not in os.environ.get('PATH', ''):
27
+ pytesseract.pytesseract.tesseract_cmd = '/usr/bin/tesseract'
28
+ except Exception as e:
29
+ st.warning(f"Tesseract configuration issue: {str(e)}")
30
 
31
  # Set page config
32
  st.set_page_config(
 
39
  # Custom CSS for styling
40
  st.markdown("""
41
  <style>
42
+ .header {
43
+ font-size: 36px;
44
+ color: #2e86c1;
45
+ text-align: center;
46
+ padding: 20px;
47
+ }
48
+ .subheader {
49
+ font-size: 24px;
50
+ color: #28b463;
51
+ border-bottom: 2px solid #f4d03f;
52
+ padding-bottom: 10px;
53
+ margin-top: 30px;
54
+ }
55
+ .stButton>button {
56
+ background-color: #28b463 !important;
57
+ color: white !important;
58
+ border-radius: 8px;
59
+ padding: 8px 20px;
60
+ transition: all 0.3s;
61
+ }
62
+ .stButton>button:hover {
63
+ background-color: #239b56 !important;
64
+ transform: scale(1.05);
65
+ }
66
+ .score-card {
67
+ background: linear-gradient(135deg, #e8f8f5, #d1f2eb);
68
+ border-radius: 15px;
69
+ padding: 20px;
70
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
71
+ margin-bottom: 20px;
72
+ }
73
+ .highlight {
74
+ background-color: #f9e79f;
75
+ padding: 5px;
76
+ border-radius: 5px;
77
+ font-weight: bold;
78
+ }
79
+ .tip-box {
80
+ background-color: #eafaf1;
81
+ border-left: 5px solid #28b463;
82
+ padding: 15px;
83
+ margin: 15px 0;
84
+ border-radius: 0 8px 8px 0;
85
+ }
86
+ .error-box {
87
+ background-color: #fdecea;
88
+ border-left: 5px solid #e74c3c;
89
+ padding: 15px;
90
+ margin: 15px 0;
91
+ border-radius: 0 8px 8px 0;
92
+ }
93
+ .experiment-card {
94
+ background: linear-gradient(135deg, #f0f7ff, #e1effe);
95
+ border-radius: 15px;
96
+ padding: 20px;
97
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
98
+ margin-bottom: 20px;
99
+ transition: all 0.3s;
100
+ }
101
+ .experiment-card:hover {
102
+ transform: translateY(-5px);
103
+ box-shadow: 0 6px 12px rgba(0,0,0,0.15);
104
+ }
105
+ .concept-box {
106
+ background-color: #ebf5fb;
107
+ border-left: 5px solid #3498db;
108
+ padding: 15px;
109
+ margin: 15px 0;
110
+ border-radius: 0 8px 8px 0;
111
+ }
112
+ [data-testid="stSidebar"] {
113
+ background: linear-gradient(180deg, #e8f8f5, #d1f2eb) !important;
114
+ }
115
+ .footer {
116
+ text-align: center;
117
+ padding: 20px;
118
+ color: #7f8c8d;
119
+ font-size: 14px;
120
+ }
121
+ .ocr-warning {
122
+ background-color: #fef9e7;
123
+ border-left: 5px solid #f1c40f;
124
+ padding: 15px;
125
+ margin: 15px 0;
126
+ border-radius: 0 8px 8px 0;
127
+ }
128
  </style>
129
  """, unsafe_allow_html=True)
130
 
 
139
  </div>
140
  """, unsafe_allow_html=True)
141
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  # Experiment templates
143
  experiments = {
144
  "Vinegar + Baking Soda": {
 
158
  # AI Query Function
159
  def query_ai(prompt):
160
  headers = {
161
+ "Authorization": f"Bearer {API_KEY}",
162
  "Content-Type": "application/json"
163
  }
164
  payload = {
 
200
  ai_response = query_ai(f"Explain the term '{term}' in simple words for a student.")
201
  if ai_response:
202
  st.markdown(f"<div class='concept-box'>{ai_response}</div>", unsafe_allow_html=True)
203
+ else:
204
+ st.warning("Couldn't retrieve definition. Please try again.")
205
 
206
  # --- Experiment Assistant Section ---
207
  if app_mode == "πŸ§ͺ Experiment Assistant":
208
+ st.markdown('<p class="subheader">πŸ” Design Your Experiment</p>', unsafe_allow_html=True)
209
+
210
+ with st.form("experiment_form"):
211
+ # Pre-fill if template selected
212
+ if selected_exp != "Custom Experiment" and selected_exp in experiments:
213
+ default_hypo = experiments[selected_exp]["hypothesis"]
214
+ concept = experiments[selected_exp]["concept"]
215
+ exp_name = selected_exp
216
+ else:
217
+ default_hypo = ""
218
+ concept = ""
219
+ exp_name = st.text_input("Experiment Name", placeholder="e.g., Effect of Temperature on Enzyme Activity")
220
+
221
+ hypo = st.text_area("Your Hypothesis", value=default_hypo,
222
+ placeholder="What do you predict will happen?")
223
+
224
+ materials = st.text_area("Materials Needed",
225
+ placeholder="List all materials needed for this experiment")
226
+
227
+ procedure = st.text_area("Procedure Steps",
228
+ placeholder="Step-by-step instructions for conducting the experiment")
229
+
230
+ submit = st.form_submit_button("πŸ” Generate Experiment Guide", use_container_width=True)
231
+
232
+ if submit:
233
+ if not exp_name or not hypo:
234
+ st.warning("Please provide at least an experiment name and hypothesis")
235
+ st.stop()
236
+
237
+ with st.spinner("Designing your experiment guide..."):
238
+ prompt = f"""
239
+ Create a comprehensive guide for a science experiment with the following details:
240
+
241
+ Experiment Name: {exp_name}
242
+ Hypothesis: {hypo}
243
+ Materials: {materials if materials else 'Not specified'}
244
+ Procedure: {procedure if procedure else 'Not specified'}
245
+
246
+ Please provide:
247
+ 1. A clear explanation of the scientific concept behind the experiment
248
+ 2. Step-by-step instructions for conducting the experiment
249
+ 3. Safety precautions
250
+ 4. Expected results and why they're expected
251
+ 5. How to interpret the results
252
+ """
253
+
254
+ explanation = query_ai(prompt)
255
+
256
+ if explanation:
257
+ st.success("βœ… Experiment Guide Generated!")
258
+ st.balloons()
259
+
260
+ # Display explanation
261
+ st.markdown("### πŸ§ͺ Experiment Guide")
262
+ st.markdown(f"<div class='tip-box'>{explanation}</div>", unsafe_allow_html=True)
263
+
264
+ # Generate PDF report
265
+ def generate_pdf_report(exp_name, hypo, explanation, materials, procedure):
266
+ pdf = FPDF()
267
+ pdf.add_page()
268
+ pdf.set_font("Arial", size=12)
269
+
270
+ # Title
271
+ pdf.set_font("Arial", 'B', 16)
272
+ pdf.cell(200, 10, txt="Science Experiment Guide", ln=True, align='C')
273
+ pdf.ln(15)
274
+
275
+ # Experiment details
276
+ pdf.set_font("Arial", 'B', 14)
277
+ pdf.cell(0, 10, txt=f"Experiment: {exp_name}", ln=True)
278
+ pdf.ln(5)
279
+
280
+ pdf.set_font("Arial", 'B', 12)
281
+ pdf.cell(0, 10, txt="Hypothesis:", ln=True)
282
+ pdf.set_font("Arial", '', 12)
283
+ pdf.multi_cell(0, 8, txt=hypo)
284
+ pdf.ln(5)
285
+
286
+ if materials:
287
+ pdf.set_font("Arial", 'B', 12)
288
+ pdf.cell(0, 10, txt="Materials:", ln=True)
289
+ pdf.set_font("Arial", '', 12)
290
+ pdf.multi_cell(0, 8, txt=materials)
291
+ pdf.ln(5)
292
+
293
+ if procedure:
294
+ pdf.set_font("Arial", 'B', 12)
295
+ pdf.cell(0, 10, txt="Procedure:", ln=True)
296
+ pdf.set_font("Arial", '', 12)
297
+ pdf.multi_cell(0, 8, txt=procedure)
298
+ pdf.ln(10)
299
+
300
+ pdf.set_font("Arial", 'B', 12)
301
+ pdf.cell(0, 10, txt="Experiment Guide:", ln=True)
302
+ pdf.set_font("Arial", '', 12)
303
+ pdf.multi_cell(0, 8, txt=explanation)
304
+
305
+ filename = f"experiment_guide_{datetime.now().strftime('%Y%m%d%H%M%S')}.pdf"
306
+ pdf.output(filename)
307
+ return filename
308
+
309
+ pdf_file = generate_pdf_report(exp_name, hypo, explanation, materials, procedure)
310
+ with open(pdf_file, "rb") as file:
311
+ st.download_button("πŸ“„ Download Experiment Guide (PDF)", file,
312
+ file_name=f"{exp_name}_guide.pdf",
313
+ use_container_width=True)
314
+
315
+ # Experiment examples
316
+ st.markdown("---")
317
+ st.markdown('<p class="subheader">πŸ”¬ Popular Science Experiments</p>', unsafe_allow_html=True)
318
+
319
+ col1, col2 = st.columns(2)
320
+ with col1:
321
+ with st.container():
322
+ st.markdown('<div class="experiment-card">', unsafe_allow_html=True)
323
+ st.markdown("#### 🧫 Vinegar + Baking Soda")
324
+ st.markdown("**Hypothesis:** Mixing vinegar and baking soda will produce bubbles due to a chemical reaction.")
325
+ st.markdown("**Concept:** Acid-base reaction producing carbon dioxide.")
326
+ if st.button("Try This Experiment", key="vinegar", use_container_width=True):
327
+ st.session_state.selected_exp = "Vinegar + Baking Soda"
328
+ st.markdown('</div>', unsafe_allow_html=True)
329
+
330
+ with st.container():
331
+ st.markdown('<div class="experiment-card">', unsafe_allow_html=True)
332
+ st.markdown("#### πŸ₯š Floating Egg")
333
+ st.markdown("**Hypothesis:** An egg will float in salt water but sink in plain water.")
334
+ st.markdown("**Concept:** Density difference between saltwater and freshwater.")
335
+ if st.button("Try This Experiment", key="egg", use_container_width=True):
336
+ st.session_state.selected_exp = "Floating Egg"
337
+ st.markdown('</div>', unsafe_allow_html=True)
338
+
339
+ with col2:
340
+ with st.container():
341
+ st.markdown('<div class="experiment-card">', unsafe_allow_html=True)
342
+ st.markdown("#### πŸ‹ Lemon Battery")
343
+ st.markdown("**Hypothesis:** A lemon can produce electricity to power a small LED.")
344
+ st.markdown("**Concept:** Chemical energy conversion to electrical energy.")
345
+ if st.button("Try This Experiment", key="lemon", use_container_width=True):
346
+ st.session_state.selected_exp = "Lemon Battery"
347
+ st.markdown('</div>', unsafe_allow_html=True)
348
+
349
+ with st.container():
350
+ st.markdown('<div class="experiment-card">', unsafe_allow_html=True)
351
+ st.markdown("#### 🌈 Rainbow in a Glass")
352
+ st.markdown("**Hypothesis:** Different sugar solutions can form colorful layers in a glass.")
353
+ st.markdown("**Concept:** Density gradient formation.")
354
+ if st.button("Try This Experiment", key="rainbow", use_container_width=True):
355
+ st.session_state.selected_exp = "Custom Experiment"
356
+ st.session_state.custom_exp = "Rainbow in a Glass"
357
+ st.session_state.custom_hypo = "Different sugar solutions will form distinct layers based on their density."
358
+ st.markdown('</div>', unsafe_allow_html=True)
359
 
360
  # --- Lab Report Analyzer Section ---
361
  else:
362
  # --- File Upload ---
363
  st.markdown('<p class="subheader">πŸ“€ Upload Your Lab Report</p>', unsafe_allow_html=True)
364
+ uploaded_file = st.file_uploader("Upload image (JPG, PNG) or PDF",
365
+ type=["jpg", "jpeg", "png", "pdf"],
366
  label_visibility="collapsed")
367
 
368
  lab_text = ""
 
379
  except Exception as e:
380
  st.error(f"Error reading PDF: {str(e)}")
381
  else:
382
+ try:
383
+ image = Image.open(io.BytesIO(file_bytes))
384
+ st.image(image, caption="Uploaded Image", width=300)
385
+
386
+ # OCR processing
387
+ with st.spinner("Extracting text from image..."):
388
+ try:
389
+ lab_text = pytesseract.image_to_string(image)
390
+ st.success("βœ… Text extracted from image!")
391
+ except pytesseract.pytesseract.TesseractNotFoundError:
392
+ st.error("""
393
+ **Tesseract OCR not found!**
394
+
395
+ To enable image text extraction:
396
+ 1. Install Tesseract OCR on your system
397
+ 2. Add it to your system PATH
398
+ 3. Restart the application
399
+
400
+ For Windows: Download from [UB-Mannheim/tesseract](https://github.com/UB-Mannheim/tesseract/wiki)
401
+ For Linux: `sudo apt install tesseract-ocr`
402
+ For Mac: `brew install tesseract`
403
+ """)
404
+ st.stop()
405
+ except Exception as e:
406
+ st.error(f"OCR Error: {str(e)}")
407
+ st.stop()
408
+ except Exception as e:
409
+ st.error(f"Error processing image: {str(e)}")
410
 
411
  # Allow text editing
412
  if lab_text:
 
460
  st.success("βœ… Analysis Complete!")
461
  st.balloons()
462
 
463
+ # Extract score using regex
464
+ score_match = re.search(r"Completeness Score:\s*(\d+)/10", result, re.IGNORECASE)
465
+ score = int(score_match.group(1)) if score_match else None
466
+
467
+ # Display score in a card
468
+ if score is not None:
469
+ with st.container():
470
+ st.markdown('<div class="score-card">', unsafe_allow_html=True)
471
+
472
+ # Create columns for score visualization
473
+ col1, col2 = st.columns([1, 3])
474
+
475
+ with col1:
476
+ st.markdown(f"<h2 style='text-align: center; color: #28b463;'>{score}/10</h2>",
477
+ unsafe_allow_html=True)
478
+ st.markdown("<h4 style='text-align: center;'>Completeness Score</h4>",
479
+ unsafe_allow_html=True)
480
+
481
+ with col2:
482
+ # Create a color gradient based on score
483
+ if score >= 8:
484
+ color = "#28b463" # Green
485
+ elif score >= 5:
486
+ color = "#f39c12" # Orange
487
+ else:
488
+ color = "#e74c3c" # Red
489
+
490
+ # Display progress bar with styling
491
+ st.progress(score/10, text=f"{score*10}% complete")
492
+ st.markdown(
493
+ f"<style>"
494
+ f".stProgress > div > div > div {{"
495
+ f" background-color: {color} !important;"
496
+ f" border-radius: 10px;"
497
+ f"}}"
498
+ f"</style>",
499
+ unsafe_allow_html=True
500
+ )
501
+
502
+ st.markdown('</div>', unsafe_allow_html=True)
503
+
504
+ # Display AI analysis with formatting
505
+ st.markdown("## πŸ“ Analysis Results")
506
+
507
+ # Split sections for better display
508
+ sections = {
509
+ "Missing Sections": None,
510
+ "Improvement Tips": None,
511
+ "Detailed Feedback": None
512
+ }
513
+
514
+ current_section = None
515
+ for line in result.split('\n'):
516
+ if "### Missing Sections:" in line:
517
+ current_section = "Missing Sections"
518
+ sections[current_section] = []
519
+ elif "### Improvement Tips:" in line:
520
+ current_section = "Improvement Tips"
521
+ sections[current_section] = []
522
+ elif "### Detailed Feedback:" in line:
523
+ current_section = "Detailed Feedback"
524
+ sections[current_section] = []
525
+ elif current_section and line.strip():
526
+ sections[current_section].append(line)
527
+
528
+ # Display each section
529
+ if sections["Missing Sections"]:
530
+ st.markdown("### πŸ” Missing Sections")
531
+ missing_text = '\n'.join(sections["Missing Sections"])
532
+ st.markdown(f'<div class="highlight">{missing_text}</div>', unsafe_allow_html=True)
533
+
534
+ if sections["Improvement Tips"]:
535
+ st.markdown("### πŸ’‘ Improvement Tips")
536
+ tips_text = '\n'.join(sections["Improvement Tips"])
537
+ st.markdown(f'<div class="tip-box">{tips_text}</div>', unsafe_allow_html=True)
538
+
539
+ if sections["Detailed Feedback"]:
540
+ st.markdown("### πŸ“‹ Detailed Feedback")
541
+ st.write('\n'.join(sections["Detailed Feedback"]))
542
+
543
+ # Show full AI response in expander
544
+ with st.expander("View Full AI Analysis"):
545
+ st.markdown(result)
546
 
547
  # --- Question Answering Section ---
548
  st.markdown("---")