awacke1 commited on
Commit
5ba8d4b
Β·
verified Β·
1 Parent(s): eb34529

Update app.backup06102025.py

Browse files
Files changed (1) hide show
  1. app.backup06102025.py +209 -147
app.backup06102025.py CHANGED
@@ -15,189 +15,251 @@ from reportlab.lib.utils import ImageReader
15
  import mistune
16
  from gtts import gTTS
17
 
18
- # Page config
19
- st.set_page_config(page_title="PDF & Code Interpreter", layout="wide", page_icon="πŸš€")
20
 
 
21
  def delete_asset(path):
 
22
  try:
23
  os.remove(path)
24
- except:
25
- pass
26
  st.rerun()
27
 
28
- # Tabs setup
29
- tab1, tab2 = st.tabs(["πŸ“„ PDF Composer", "πŸ§ͺ Code Interpreter"])
 
 
 
 
 
 
30
 
31
- with tab1:
32
- st.header("πŸ“„ PDF Composer & Voice Generator πŸš€")
33
-
34
- # Sidebar PDF text settings
35
- columns = st.sidebar.slider("Text columns", 1, 3, 1)
36
- font_family = st.sidebar.selectbox("Font", ["Helvetica","Times-Roman","Courier"])
37
- font_size = st.sidebar.slider("Font size", 6, 24, 12)
38
-
39
- # Markdown input
40
- md_file = st.file_uploader("Upload Markdown (.md)", type=["md"])
41
- if md_file:
42
- md_text = md_file.getvalue().decode("utf-8")
43
- stem = Path(md_file.name).stem
44
  else:
45
- md_text = st.text_area("Or enter markdown text directly", height=200)
46
- stem = datetime.now().strftime('%Y%m%d_%H%M%S')
47
 
48
- # Convert Markdown to plain text
49
  renderer = mistune.HTMLRenderer()
50
  markdown = mistune.create_markdown(renderer=renderer)
51
  html = markdown(md_text or "")
52
  plain_text = re.sub(r'<[^>]+>', '', html)
53
 
54
- # Voice settings
55
- languages = {"English (US)": "en", "English (UK)": "en-uk", "Spanish": "es"}
56
- voice_choice = st.selectbox("Voice Language", list(languages.keys()))
57
- voice_lang = languages[voice_choice]
58
- slow = st.checkbox("Slow Speech")
59
-
60
- if st.button("πŸ”Š Generate & Download Voice MP3 from Text"):
61
- if plain_text.strip():
62
- voice_file = f"{stem}.mp3"
63
- tts = gTTS(text=plain_text, lang=voice_lang, slow=slow)
64
- tts.save(voice_file)
65
- st.audio(voice_file)
66
- with open(voice_file, 'rb') as mp3:
67
- st.download_button("πŸ“₯ Download MP3", data=mp3, file_name=voice_file, mime="audio/mpeg")
68
- else:
69
- st.warning("No text to generate voice from.")
70
-
71
- # Image uploads and ordering
72
- imgs = st.file_uploader("Upload Images for PDF", type=["png", "jpg", "jpeg"], accept_multiple_files=True)
73
- ordered_images = []
74
- if imgs:
75
- df_imgs = pd.DataFrame([{"name": f.name, "order": i} for i, f in enumerate(imgs)])
76
- edited = st.data_editor(df_imgs, use_container_width=True)
77
- for _, row in edited.sort_values("order").iterrows():
78
- for f in imgs:
79
- if f.name == row['name']:
80
- ordered_images.append(f)
81
- break
82
-
83
- if st.button("πŸ–‹οΈ Generate PDF with Markdown & Images"):
84
- buf = io.BytesIO()
85
- c = canvas.Canvas(buf)
86
-
87
- # Render text with columns
88
- page_w, page_h = letter
89
- margin = 40
90
- gutter = 20
91
- col_w = (page_w - 2*margin - (columns-1)*gutter) / columns
92
- c.setFont(font_family, font_size)
93
- line_height = font_size * 1.2
94
- col = 0
95
- x = margin
96
- y = page_h - margin
97
- wrap_width = int(col_w / (font_size * 0.6))
98
-
99
- for paragraph in plain_text.split("\n"):
100
- for line in textwrap.wrap(paragraph, wrap_width):
101
- if y < margin:
102
- col += 1
103
- if col >= columns:
104
- c.showPage()
105
- c.setFont(font_family, font_size)
106
- col = 0
107
- x = margin + col*(col_w+gutter)
108
- y = page_h - margin
109
- c.drawString(x, y, line)
110
- y -= line_height
111
  y -= line_height
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
- # Autosize pages to each image
114
- for img_f in ordered_images:
115
- try:
116
- img = Image.open(img_f)
117
- w, h = img.size
118
- c.showPage()
119
- c.setPageSize((w, h))
120
- c.drawImage(ImageReader(img), 0, 0, w, h, preserveAspectRatio=True, mask='auto')
121
- except:
122
- continue
123
-
124
- c.save()
125
- buf.seek(0)
126
- pdf_name = f"{stem}.pdf"
127
- st.download_button("⬇️ Download PDF", data=buf, file_name=pdf_name, mime="application/pdf")
128
 
 
 
 
129
  st.markdown("---")
130
  st.subheader("πŸ“‚ Available Assets")
131
  assets = sorted(glob.glob("*.*"))
132
- for a in assets:
133
- ext = a.split('.')[-1].lower()
 
 
 
 
134
  cols = st.columns([3, 1, 1])
135
- cols[0].write(a)
136
- if ext == 'pdf':
137
- with open(a, 'rb') as fp:
138
- cols[1].download_button("πŸ“₯", data=fp, file_name=a, mime="application/pdf")
139
- elif ext == 'mp3':
140
- cols[1].audio(a)
141
- with open(a, 'rb') as mp3:
142
- cols[1].download_button("πŸ“₯", data=mp3, file_name=a, mime="audio/mpeg")
143
- cols[2].button("πŸ—‘οΈ", key=f"del_{a}", on_click=delete_asset, args=(a,))
144
-
145
- with tab2:
 
 
 
 
 
 
 
146
  st.header("πŸ§ͺ Python Code Executor & Demo")
147
- import io, sys
148
- from contextlib import redirect_stdout
149
-
150
- DEFAULT_CODE = '''import streamlit as st
151
- import random
152
-
153
- st.title("πŸ“Š Demo App")
154
- st.markdown("Random number and color demo")
155
-
156
- col1, col2 = st.columns(2)
157
- with col1:
158
- num = st.number_input("Number:", 1, 100, 10)
159
- mul = st.slider("Multiplier:", 1, 10, 2)
160
- if st.button("Calc"):
161
- st.write(num * mul)
162
- with col2:
163
- color = st.color_picker("Pick color","#ff0000")
164
- st.markdown(f'<div style="background:{color};padding:10px;">Color</div>', unsafe_allow_html=True)
165
- ''' # noqa
166
-
167
- def extract_python_code(md: str) -> list:
168
- return re.findall(r"```python\s*(.*?)```", md, re.DOTALL)
169
-
170
- def execute_code(code: str) -> tuple:
171
- buf = io.StringIO(); local_vars = {}
172
  try:
173
- with redirect_stdout(buf): exec(code, {}, local_vars)
174
- return buf.getvalue(), None
 
175
  except Exception as e:
176
  return None, str(e)
177
 
178
- up = st.file_uploader("Upload .py or .md", type=['py', 'md'])
 
179
  if 'code' not in st.session_state:
180
  st.session_state.code = DEFAULT_CODE
181
- if up:
182
- text = up.getvalue().decode()
183
- if up.type == 'text/markdown':
184
- codes = extract_python_code(text)
 
 
185
  st.session_state.code = codes[0] if codes else ''
186
  else:
187
- st.session_state.code = text
188
  st.code(st.session_state.code, language='python')
189
  else:
190
  st.session_state.code = st.text_area("πŸ’» Code Editor", value=st.session_state.code, height=300)
191
 
192
- c1, c2 = st.columns([1, 1])
193
- if c1.button("▢️ Run Code"):
194
- out, err = execute_code(st.session_state.code)
195
  if err:
196
  st.error(err)
197
- elif out:
198
- st.code(out)
199
  else:
200
- st.success("Executed with no output.")
201
- if c2.button("πŸ—‘οΈ Clear Code"):
202
  st.session_state.code = ''
203
  st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  import mistune
16
  from gtts import gTTS
17
 
18
+ # --- Helper Functions ---
 
19
 
20
+ # πŸ—‘οΈ Deletes a specified file and reruns the app.
21
  def delete_asset(path):
22
+ """Safely deletes a file if it exists and reruns the Streamlit app."""
23
  try:
24
  os.remove(path)
25
+ except OSError as e:
26
+ st.error(f"Error deleting file {path}: {e}")
27
  st.rerun()
28
 
29
+ # πŸ“₯ Gets text input from either a file upload or a text area.
30
+ def get_text_input(file_uploader_label, accepted_types, text_area_label):
31
+ """
32
+ Provides UI for uploading a text file or entering text manually.
33
+ Returns the text content and a filename stem.
34
+ """
35
+ md_text = ""
36
+ stem = datetime.now().strftime('%Y%m%d_%H%M%S')
37
 
38
+ uploaded_file = st.file_uploader(file_uploader_label, type=accepted_types)
39
+ if uploaded_file:
40
+ md_text = uploaded_file.getvalue().decode("utf-8")
41
+ stem = Path(uploaded_file.name).stem
 
 
 
 
 
 
 
 
 
42
  else:
43
+ md_text = st.text_area(text_area_label, height=200)
 
44
 
45
+ # Convert markdown to plain text for processing
46
  renderer = mistune.HTMLRenderer()
47
  markdown = mistune.create_markdown(renderer=renderer)
48
  html = markdown(md_text or "")
49
  plain_text = re.sub(r'<[^>]+>', '', html)
50
 
51
+ return plain_text, stem
52
+
53
+ # πŸ—£οΈ Generates an MP3 voice file from text using gTTS.
54
+ def generate_voice_file(text, lang, is_slow, filename):
55
+ """
56
+ Creates an MP3 from text, saves it, and provides it for playback and download.
57
+ """
58
+ if not text.strip():
59
+ st.warning("No text to generate voice from.")
60
+ return
61
+
62
+ voice_file_path = f"{filename}.mp3"
63
+ try:
64
+ tts = gTTS(text=text, lang=lang, slow=is_slow)
65
+ tts.save(voice_file_path)
66
+ st.audio(voice_file_path)
67
+ with open(voice_file_path, 'rb') as fp:
68
+ st.download_button("πŸ“₯ Download MP3", data=fp, file_name=voice_file_path, mime="audio/mpeg")
69
+ except Exception as e:
70
+ st.error(f"Failed to generate audio: {e}")
71
+
72
+ # πŸ“„ Creates a PDF document from text and images.
73
+ def generate_pdf(text_content, images, pdf_params):
74
+ """
75
+ Generates a PDF buffer from text and a list of images based on specified parameters.
76
+ """
77
+ buf = io.BytesIO()
78
+ c = canvas.Canvas(buf)
79
+ page_w, page_h = letter
80
+ margin = 40
81
+ gutter = 20
82
+ col_w = (page_w - 2 * margin - (pdf_params['columns'] - 1) * gutter) / pdf_params['columns']
83
+
84
+ c.setFont(pdf_params['font_family'], pdf_params['font_size'])
85
+ line_height = pdf_params['font_size'] * 1.2
86
+ wrap_width = int(col_w / (pdf_params['font_size'] * 0.6))
87
+
88
+ y = page_h - margin
89
+ col_idx = 0
90
+
91
+ # --- Render Text ---
92
+ for paragraph in text_content.split("\n"):
93
+ wrapped_lines = textwrap.wrap(paragraph, wrap_width) if paragraph.strip() else [""]
94
+ for line in wrapped_lines:
95
+ if y < margin:
96
+ col_idx += 1
97
+ if col_idx >= pdf_params['columns']:
98
+ c.showPage()
99
+ c.setFont(pdf_params['font_family'], pdf_params['font_size'])
100
+ col_idx = 0
101
+ y = page_h - margin
102
+
103
+ x = margin + col_idx * (col_w + gutter)
104
+ c.drawString(x, y, line)
 
 
 
105
  y -= line_height
106
+ y -= line_height # Add extra space for paragraph breaks
107
+
108
+ # --- Render Images ---
109
+ for img_file in images:
110
+ try:
111
+ img = Image.open(img_file)
112
+ w, h = img.size
113
+ c.showPage()
114
+ c.setPageSize((w, h))
115
+ c.drawImage(ImageReader(img), 0, 0, w, h, preserveAspectRatio=True, mask='auto')
116
+ except Exception as e:
117
+ st.warning(f"Could not process image {img_file.name}: {e}")
118
+ continue
119
 
120
+ c.save()
121
+ buf.seek(0)
122
+ return buf
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
+ # πŸ—‚οΈ Displays a list of generated assets with download/delete options.
125
+ def show_asset_manager():
126
+ """Scans for local files and displays them with management controls."""
127
  st.markdown("---")
128
  st.subheader("πŸ“‚ Available Assets")
129
  assets = sorted(glob.glob("*.*"))
130
+ if not assets:
131
+ st.info("No assets generated yet.")
132
+ return
133
+
134
+ for asset_path in assets:
135
+ ext = asset_path.split('.')[-1].lower()
136
  cols = st.columns([3, 1, 1])
137
+ cols[0].write(asset_path)
138
+
139
+ try:
140
+ with open(asset_path, 'rb') as fp:
141
+ file_bytes = fp.read()
142
+
143
+ if ext == 'pdf':
144
+ cols[1].download_button("πŸ“₯", data=file_bytes, file_name=asset_path, mime="application/pdf", key=f"dl_{asset_path}")
145
+ elif ext == 'mp3':
146
+ cols[1].audio(file_bytes)
147
+ except Exception as e:
148
+ cols[1].error("Error reading file.")
149
+
150
+ cols[2].button("πŸ—‘οΈ", key=f"del_{asset_path}", on_click=delete_asset, args=(asset_path,))
151
+
152
+ # οΏ½ Renders the entire UI and logic for the Python code interpreter.
153
+ def render_code_interpreter():
154
+ """Sets up the UI and execution logic for the code interpreter tab."""
155
  st.header("πŸ§ͺ Python Code Executor & Demo")
156
+
157
+ # --- Nested Helper Functions for this Tab ---
158
+ def extract_python_code(md_text):
159
+ return re.findall(r"```python\s*(.*?)```", md_text, re.DOTALL)
160
+
161
+ def execute_code(code_str):
162
+ output_buffer = io.StringIO()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  try:
164
+ with redirect_stdout(output_buffer):
165
+ exec(code_str, {})
166
+ return output_buffer.getvalue(), None
167
  except Exception as e:
168
  return None, str(e)
169
 
170
+ # --- Main Logic for the Tab ---
171
+ DEFAULT_CODE = "import streamlit as st\n\nst.balloons()\nst.write('Hello, World!')"
172
  if 'code' not in st.session_state:
173
  st.session_state.code = DEFAULT_CODE
174
+
175
+ uploaded_file = st.file_uploader("Upload .py or .md", type=['py', 'md'])
176
+ if uploaded_file:
177
+ file_content = uploaded_file.getvalue().decode()
178
+ if uploaded_file.type == 'text/markdown':
179
+ codes = extract_python_code(file_content)
180
  st.session_state.code = codes[0] if codes else ''
181
  else:
182
+ st.session_state.code = file_content
183
  st.code(st.session_state.code, language='python')
184
  else:
185
  st.session_state.code = st.text_area("πŸ’» Code Editor", value=st.session_state.code, height=300)
186
 
187
+ c1, c2 = st.columns(2)
188
+ if c1.button("▢️ Run Code", use_container_width=True):
189
+ output, err = execute_code(st.session_state.code)
190
  if err:
191
  st.error(err)
192
+ elif output:
193
+ st.code(output, language='text')
194
  else:
195
+ st.success("Executed successfully with no output.")
196
+ if c2.button("πŸ—‘οΈ Clear Code", use_container_width=True):
197
  st.session_state.code = ''
198
  st.rerun()
199
+
200
+ # --- Main App ---
201
+ def main():
202
+ """Main function to run the Streamlit application."""
203
+ st.set_page_config(page_title="PDF & Code Interpreter", layout="wide", page_icon="πŸš€")
204
+
205
+ tab1, tab2 = st.tabs(["πŸ“„ PDF Composer", "πŸ§ͺ Code Interpreter"])
206
+
207
+ with tab1:
208
+ st.header("πŸ“„ PDF Composer & Voice Generator πŸš€")
209
+
210
+ # --- Sidebar Controls ---
211
+ st.sidebar.title("PDF Settings")
212
+ pdf_params = {
213
+ 'columns': st.sidebar.slider("Text columns", 1, 3, 1),
214
+ 'font_family': st.sidebar.selectbox("Font", ["Helvetica", "Times-Roman", "Courier"]),
215
+ 'font_size': st.sidebar.slider("Font size", 6, 24, 12),
216
+ }
217
+
218
+ # --- Main UI ---
219
+ plain_text, filename_stem = get_text_input(
220
+ "Upload Markdown (.md)", ["md"], "Or enter markdown text directly"
221
+ )
222
+
223
+ st.subheader("πŸ—£οΈ Voice Generation")
224
+ languages = {"English (US)": "en", "English (UK)": "en-uk", "Spanish": "es"}
225
+ voice_choice = st.selectbox("Voice Language", list(languages.keys()))
226
+ slow_speech = st.checkbox("Slow Speech")
227
+
228
+ if st.button("πŸ”Š Generate Voice MP3"):
229
+ generate_voice_file(plain_text, languages[voice_choice], slow_speech, filename_stem)
230
+
231
+ st.subheader("πŸ–ΌοΈ Image Upload")
232
+ uploaded_images = st.file_uploader(
233
+ "Upload Images for PDF", type=["png", "jpg", "jpeg"], accept_multiple_files=True
234
+ )
235
+
236
+ ordered_images = []
237
+ if uploaded_images:
238
+ df_imgs = pd.DataFrame([{"name": f.name, "order": i} for i, f in enumerate(uploaded_images)])
239
+ edited_df = st.data_editor(df_imgs, use_container_width=True, key="img_order_editor")
240
+
241
+ # Create a map for quick lookup
242
+ image_map = {f.name: f for f in uploaded_images}
243
+
244
+ # Sort and append images based on the edited order
245
+ for _, row in edited_df.sort_values("order").iterrows():
246
+ if row['name'] in image_map:
247
+ ordered_images.append(image_map[row['name']])
248
+
249
+ st.subheader("πŸ–‹οΈ PDF Generation")
250
+ if st.button("Generate PDF with Markdown & Images"):
251
+ pdf_buffer = generate_pdf(plain_text, ordered_images, pdf_params)
252
+ st.download_button(
253
+ "⬇️ Download PDF",
254
+ data=pdf_buffer,
255
+ file_name=f"{filename_stem}.pdf",
256
+ mime="application/pdf"
257
+ )
258
+
259
+ show_asset_manager()
260
+
261
+ with tab2:
262
+ render_code_interpreter()
263
+
264
+ if __name__ == "__main__":
265
+ main()