Rupesx007 commited on
Commit
30b747b
·
verified ·
1 Parent(s): 114ccca

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +193 -231
app.py CHANGED
@@ -1,232 +1,194 @@
1
- import streamlit as st
2
- from database import db
3
- import html
4
- import re
5
- import streamlit.components.v1 as components
6
-
7
- def get_text_from_firebase(text_id):
8
- """Retrieve text from Firebase Firestore."""
9
- try:
10
- doc = db.collection("texts").document(text_id).get()
11
- if doc.exists:
12
- return doc.to_dict()["content"]
13
- else:
14
- return None
15
- except Exception as e:
16
- st.error(f"Error retrieving text from Firebase: {e}")
17
- return None
18
-
19
- def process_text(text):
20
- """Clean and structure the extracted text."""
21
- # Preserve paragraph breaks
22
- text = re.sub(r'\n\s*\n', '\n\n', text)
23
-
24
- # Convert bullet points to HTML lists
25
- text = re.sub(r'^(\s*•\s+)(.*)$', r'<li>\2</li>', text, flags=re.MULTILINE)
26
- text = re.sub(r'(<li>.*</li>\n)+', r'<ul>\g<0></ul>\n', text)
27
-
28
- # Detect headings (lines ending with colons or in all caps)
29
- text = re.sub(r'^([A-Z][A-Z\s]+:?)\s*$', r'<h3 class="heading">\1</h3>', text, flags=re.MULTILINE)
30
-
31
- # Convert URLs to clickable links
32
- text = re.sub(r'(https?://\S+)', r'<a href="\1" target="_blank">\1</a>', text)
33
-
34
- # Preserve original paragraph structure
35
- paragraphs = text.split('\n\n')
36
- processed = []
37
-
38
- for p in paragraphs:
39
- p = p.strip()
40
- if p:
41
- if p.startswith('<ul>') or p.startswith('<h3'):
42
- processed.append(p)
43
- else:
44
- processed.append(f'<p>{p}</p>')
45
-
46
- return '\n'.join(processed)
47
-
48
- def main():
49
- st.title("Accessibility-Optimized Viewer")
50
-
51
- # Ask user for Document ID
52
- text_id = st.text_input("Enter Document ID to view text:")
53
-
54
- if text_id:
55
- text = get_text_from_firebase(text_id)
56
- if text:
57
- # Accessibility Controls
58
- col1, col2, col3 = st.columns(3)
59
- with col1:
60
- font_size = st.slider("Font Size", 8, 48, 16)
61
- line_height = st.slider("Line Height", 1.0, 2.5, 1.5)
62
- with col2:
63
- font_color = st.color_picker("Text Color", "#000000")
64
- bg_color = st.color_picker("Background Color", "#FFFFFF")
65
- with col3:
66
- contrast_mode = st.checkbox("High Contrast Mode")
67
- dyslexia_font = st.checkbox("Dyslexia-Friendly Font")
68
-
69
- # Apply contrast mode
70
- if contrast_mode:
71
- font_color = "#FFFFFF"
72
- bg_color = "#000000"
73
-
74
- # Create CSS styles
75
- font_family = "Arial" if not dyslexia_font else "OpenDyslexic, sans-serif"
76
-
77
- custom_css = f"""
78
- <style>
79
- .content {{
80
- font-size: {font_size}px;
81
- color: {font_color};
82
- background-color: {bg_color};
83
- line-height: {line_height};
84
- font-family: {font_family};
85
- padding: 20px;
86
- border-radius: 10px;
87
- margin: 10px 0;
88
- white-space: pre-wrap;
89
- word-wrap: break-word;
90
- hyphens: auto;
91
- }}
92
-
93
- .content p {{
94
- margin: 0.8em 0;
95
- }}
96
-
97
- .content ul {{
98
- margin: 0.8em 20px;
99
- padding-left: 20px;
100
- list-style-type: disc;
101
- }}
102
-
103
- .content h3 {{
104
- font-size: 1.2em;
105
- margin: 1.2em 0 0.5em;
106
- padding-bottom: 3px;
107
- border-bottom: 2px solid {font_color};
108
- }}
109
-
110
- .content a {{
111
- color: {font_color};
112
- text-decoration: underline;
113
- word-break: break-all;
114
- }}
115
-
116
- @keyframes highlight {{ 0% {{background: yellow;}} 100% {{background: transparent;}} }}
117
- .highlight {{ animation: highlight 2s; }}
118
- </style>
119
- """
120
- st.markdown(custom_css, unsafe_allow_html=True)
121
-
122
- # Process and display text
123
- processed_text = process_text(html.escape(text))
124
-
125
- # Create a div with an ID that can be targeted by ResponsiveVoice
126
- content_html = f'<div id="content-to-speak" class="content">{processed_text}</div>'
127
- st.markdown(content_html, unsafe_allow_html=True)
128
-
129
- # Additional accessibility features
130
- with st.expander("More Accessibility Options"):
131
- col1, col2 = st.columns(2)
132
- with col1:
133
- letter_spacing = st.slider("Letter Spacing (px)", -1, 5, 0)
134
- word_spacing = st.slider("Word Spacing (px)", 0, 10, 0)
135
- with col2:
136
- text_align = st.selectbox("Text Alignment", ["left", "justify", "center"])
137
- text_transform = st.selectbox("Text Case", ["none", "uppercase", "lowercase"])
138
-
139
- # Update CSS
140
- st.markdown(f"""
141
- <style>
142
- .content {{
143
- letter-spacing: {letter_spacing}px;
144
- word-spacing: {word_spacing}px;
145
- text-align: {text_align};
146
- text-transform: {text_transform};
147
- }}
148
- </style>
149
- """, unsafe_allow_html=True)
150
-
151
- # Voice selection for ResponsiveVoice
152
- st.subheader("Text-to-Speech Options")
153
- voices = ["UK English Female", "UK English Male", "US English Female", "US English Male",
154
- "Spanish Female", "Spanish Male", "French Female", "French Male",
155
- "German Female", "German Male", "Italian Female", "Italian Male"]
156
- selected_voice = st.selectbox("Select Voice", voices, index=0)
157
-
158
- # ResponsiveVoice integration
159
- # Modified ResponsiveVoice integration
160
- responsive_voice_js = f"""
161
- <script src="https://code.responsivevoice.org/responsivevoice.js?key=9MY6AzBd"></script>
162
-
163
- <div id="voice-controls">
164
- <button id="speak-button">Read Aloud</button>
165
- <button id="pause-button">Pause</button>
166
- <button id="resume-button">Resume</button>
167
- <button id="stop-button">Stop</button>
168
- </div>
169
-
170
- <script>
171
- document.addEventListener('DOMContentLoaded', function() {{
172
- // Check if ResponsiveVoice is loaded
173
- if(typeof responsiveVoice === 'undefined') {{
174
- console.error('ResponsiveVoice not loaded!');
175
- return;
176
- }}
177
-
178
- const getContent = () => {{
179
- const el = document.getElementById('content-to-speak');
180
- return el ? el.innerText : '';
181
- }};
182
-
183
- document.getElementById('speak-button').addEventListener('click', () => {{
184
- responsiveVoice.speak(getContent(), "{selected_voice}");
185
- }});
186
-
187
- document.getElementById('pause-button').addEventListener('click', () => {{
188
- responsiveVoice.pause();
189
- }});
190
-
191
- document.getElementById('resume-button').addEventListener('click', () => {{
192
- responsiveVoice.resume();
193
- }});
194
-
195
- document.getElementById('stop-button').addEventListener('click', () => {{
196
- responsiveVoice.cancel();
197
- }});
198
-
199
- // Text selection handler
200
- document.addEventListener('mouseup', function() {{
201
- const selection = window.getSelection().toString().trim();
202
- if(selection) {{
203
- responsiveVoice.cancel();
204
- responsiveVoice.speak(selection, "{selected_voice}");
205
- }}
206
- }});
207
- }});
208
- </script>
209
-
210
- <style>
211
- #voice-controls button {{
212
- margin: 2px;
213
- padding: 6px 12px;
214
- background-color: #4CAF50;
215
- color: white;
216
- border: none;
217
- border-radius: 4px;
218
- cursor: pointer;
219
- }}
220
- </style>
221
- """
222
-
223
- # Use Streamlit component to inject the JavaScript
224
- components.html(responsive_voice_js, height=70)
225
-
226
- st.info("You can now click 'Read Aloud' to have the text read to you, or select specific text to have it read.")
227
- else:
228
- st.error("Text not found. Please check the ID.")
229
-
230
-
231
- if __name__ == "__main__":
232
  main()
 
1
+ import streamlit as st
2
+ from database import db
3
+ from firebase_admin import firestore
4
+ from PyPDF2 import PdfReader
5
+ import easyocr
6
+ from PIL import Image
7
+ from io import BytesIO
8
+ import random
9
+ import string
10
+ import base64
11
+ import os
12
+ import numpy as np
13
+ from mistralai import Mistral
14
+
15
+ def generate_readable_id(length=5):
16
+ """Generate a random alphanumeric ID."""
17
+ characters = string.ascii_letters + string.digits # A-Z, a-z, 0-9
18
+ return ''.join(random.choice(characters) for _ in range(length))
19
+
20
+
21
+ def encode_image(image_file):
22
+ """Encode image to base64."""
23
+ return base64.b64encode(image_file.read()).decode('utf-8')
24
+
25
+ def process_mistral_ocr(file, api_key):
26
+ """Process OCR using Mistral API."""
27
+ client = Mistral(api_key=api_key)
28
+
29
+ if file.type == "application/pdf":
30
+ # Upload the PDF
31
+ uploaded_pdf = client.files.upload(
32
+ file={
33
+ "file_name": file.name,
34
+ "content": file.getvalue(),
35
+ },
36
+ purpose="ocr"
37
+ )
38
+
39
+ # Get OCR results
40
+ ocr_response = client.ocr.process(
41
+ model="mistral-ocr-latest",
42
+ document={
43
+ "type": "document_url",
44
+ "document_url": client.files.get_signed_url(file_id=uploaded_pdf.id).url,
45
+ }
46
+ )
47
+ else:
48
+ # Process Image
49
+ base64_image = encode_image(file)
50
+ ocr_response = client.ocr.process(
51
+ model="mistral-ocr-latest",
52
+ document={
53
+ "type": "image_url",
54
+ "image_url": f"data:image/jpeg;base64,{base64_image}"
55
+ }
56
+ )
57
+
58
+ return ocr_response["text"]
59
+
60
+ def extract_text_from_pdf(file):
61
+ """Extract text from a PDF file using PyPDF2."""
62
+ reader = PdfReader(file)
63
+ text = ""
64
+ for page in reader.pages:
65
+ text += page.extract_text() or ""
66
+ return text
67
+
68
+ def extract_text_from_image(image):
69
+ """Extract text from an image using EasyOCR."""
70
+ # Convert PIL Image to numpy array for EasyOCR
71
+ img_array = np.array(image)
72
+
73
+ reader = easyocr.Reader(['en'], gpu=False)
74
+ results = reader.readtext(img_array)
75
+ return " ".join([result[1] for result in results])
76
+
77
+ def save_text_to_firebase(text):
78
+ """Save text to Firebase Firestore."""
79
+ try:
80
+ # Generate a readable ID for the text
81
+ text_id = generate_readable_id()
82
+
83
+ # Save the text to Firestore
84
+ db.collection("texts").document(text_id).set({
85
+ "content": text,
86
+ "created_at": firestore.SERVER_TIMESTAMP
87
+ })
88
+ return text_id
89
+ except Exception as e:
90
+ st.error(f"Error saving text to Firebase: {e}")
91
+ return None
92
+
93
+ def main():
94
+ st.title("Text Upload App")
95
+
96
+ # Sidebar for API key configuration
97
+ with st.sidebar:
98
+ st.header("OCR Configuration")
99
+ # Mistral API key input
100
+ mistral_api_key = st.text_input("Enter Mistral API Key:", type="password")
101
+ # Save API key to session state
102
+ if mistral_api_key:
103
+ st.session_state['mistral_api_key'] = mistral_api_key
104
+
105
+ # Select OCR method
106
+ ocr_method = st.radio("Choose OCR method:", ("EasyOCR (Default)", "Mistral OCR (Requires API Key)"))
107
+
108
+ # Select input method
109
+ option = st.radio("Choose input method:", ("Upload Document", "Take Picture"))
110
+
111
+ if option == "Upload Document":
112
+ uploaded_file = st.file_uploader("Upload PDF or Image", type=["pdf", "png", "jpg", "jpeg"])
113
+
114
+ if uploaded_file:
115
+ with st.spinner("Processing document..."):
116
+ if ocr_method == "Mistral OCR (Requires API Key)":
117
+ # Check if API key is available
118
+ if 'mistral_api_key' not in st.session_state or not st.session_state['mistral_api_key']:
119
+ st.error("Please provide a Mistral API key in the sidebar to use Mistral OCR")
120
+ else:
121
+ try:
122
+ # Use Mistral OCR
123
+ text = process_mistral_ocr(uploaded_file, st.session_state['mistral_api_key'])
124
+ except Exception as e:
125
+ st.error(f"Error processing with Mistral OCR: {e}")
126
+ st.info("Falling back to default OCR method...")
127
+ # Fallback to traditional methods
128
+ if uploaded_file.type == "application/pdf":
129
+ text = extract_text_from_pdf(uploaded_file)
130
+ else:
131
+ # Reset file pointer to beginning
132
+ uploaded_file.seek(0)
133
+ image = Image.open(uploaded_file)
134
+ text = extract_text_from_image(image)
135
+ else:
136
+ # Use traditional methods
137
+ if uploaded_file.type == "application/pdf":
138
+ text = extract_text_from_pdf(uploaded_file)
139
+ else:
140
+ # Reset file pointer to beginning
141
+ uploaded_file.seek(0)
142
+ image = Image.open(uploaded_file)
143
+ text = extract_text_from_image(image)
144
+
145
+ if 'text' in locals() and text and text.strip():
146
+ # Show extracted text
147
+ with st.expander("View Extracted Text"):
148
+ st.write(text)
149
+
150
+ # Save text to Firebase
151
+ text_id = save_text_to_firebase(text)
152
+ if text_id:
153
+ st.success(f"Text extracted! Your Document ID: **{text_id}**")
154
+ else:
155
+ st.warning("No text could be extracted")
156
+
157
+ elif option == "Take Picture":
158
+ picture = st.camera_input("Take a picture")
159
+
160
+ if picture:
161
+ with st.spinner("Processing image..."):
162
+ if ocr_method == "Mistral OCR (Requires API Key)":
163
+ # Check if API key is available
164
+ if 'mistral_api_key' not in st.session_state or not st.session_state['mistral_api_key']:
165
+ st.error("Please provide a Mistral API key in the sidebar to use Mistral OCR")
166
+ else:
167
+ try:
168
+ # Use Mistral OCR
169
+ text = process_mistral_ocr(picture, st.session_state['mistral_api_key'])
170
+ except Exception as e:
171
+ st.error(f"Error processing with Mistral OCR: {e}")
172
+ st.info("Falling back to default OCR method...")
173
+ # Fallback to EasyOCR
174
+ image = Image.open(BytesIO(picture.getvalue()))
175
+ text = extract_text_from_image(image)
176
+ else:
177
+ # Use EasyOCR
178
+ image = Image.open(BytesIO(picture.getvalue()))
179
+ text = extract_text_from_image(image)
180
+
181
+ if 'text' in locals() and text and text.strip():
182
+ # Show extracted text
183
+ with st.expander("View Extracted Text"):
184
+ st.write(text)
185
+
186
+ # Save text to Firebase
187
+ text_id = save_text_to_firebase(text)
188
+ if text_id:
189
+ st.success(f"Text extracted! Your Document ID: **{text_id}**")
190
+ else:
191
+ st.warning("No text could be extracted")
192
+
193
+ if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  main()