Marek4321 commited on
Commit
95fddec
·
verified ·
1 Parent(s): 5ba47a2

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +292 -0
app.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os
3
+ import time
4
+ import logging
5
+ import tempfile
6
+ from pptx import Presentation
7
+ from pptx.util import Inches
8
+ from pptx.dml.color import RGBColor
9
+ from pptx.enum.text import PP_ALIGN
10
+ import openai
11
+ import requests
12
+ from requests.exceptions import RequestException
13
+ import base64
14
+
15
+ # Konfiguracja logowania
16
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
17
+ logger = logging.getLogger(__name__)
18
+
19
+ # Funkcja do usuwania formatowania Markdown
20
+ def remove_markdown_formatting(text):
21
+ return text.replace('**', '').replace('#', '').strip()
22
+
23
+ # Funkcja do podziału tekstu na moduły (slajdy)
24
+ def split_into_modules(content):
25
+ modules = content.split('####')
26
+ modules = [module.strip() for module in modules if module.strip()]
27
+
28
+ # Logowanie modułów do debugowania
29
+ for i, module in enumerate(modules):
30
+ logger.info(f"Moduł {i + 1}: {module[:100]}...") # Logowanie pierwszych 100 znaków modułu
31
+
32
+ return modules
33
+
34
+ # Funkcja do tworzenia prezentacji z modułów
35
+ def create_ppt_from_modules(modules, output_file, aspect_ratio="16:9"):
36
+ prs = Presentation()
37
+
38
+ # Ustawienie formatu slajdów
39
+ if aspect_ratio == "4:3":
40
+ prs.slide_width = Inches(10)
41
+ prs.slide_height = Inches(7.5)
42
+ else: # 16:9
43
+ prs.slide_width = Inches(13.33)
44
+ prs.slide_height = Inches(7.5)
45
+
46
+ for module in modules:
47
+ try:
48
+ slide = prs.slides.add_slide(prs.slide_layouts[1])
49
+ title, *body = module.split('\n', 1)
50
+
51
+ # Formatowanie tytułu slajdu
52
+ title_shape = slide.shapes.title
53
+ title_shape.text = remove_markdown_formatting(title)
54
+ title_paragraph = title_shape.text_frame.paragraphs[0]
55
+ title_paragraph.font.size = Inches(0.5) # Rozmiar tytułu
56
+ title_paragraph.font.name = 'Helvetica'
57
+ title_paragraph.alignment = PP_ALIGN.LEFT
58
+
59
+ # Formatowanie treści slajdu
60
+ if body:
61
+ content = remove_markdown_formatting(body[0].strip())
62
+ text_frame = slide.placeholders[1].text_frame
63
+ text_frame.text = content
64
+
65
+ for paragraph in text_frame.paragraphs:
66
+ if paragraph.text.strip(): # Sprawdź, czy akapit ma tekst
67
+ paragraph.font.size = Inches(0.25) # Rozmiar tekstu
68
+ paragraph.font.name = 'Helvetica'
69
+ paragraph.alignment = PP_ALIGN.LEFT
70
+ else:
71
+ logger.warning(f"Pusty akapit w module: {title}")
72
+ else:
73
+ logger.warning(f"Brak treści w module: {title}")
74
+
75
+ except Exception as e:
76
+ logger.error(f"Błąd podczas przetwarzania modułu: {module}. Błąd: {e}")
77
+ st.error(f"Błąd podczas przetwarzania modułu: {module}. Błąd: {e}")
78
+
79
+ prs.save(output_file)
80
+ logger.info(f"Prezentacja została zapisana jako {output_file}")
81
+
82
+ # Funkcja do generowania treści prezentacji za pomocą OpenAI
83
+ def generate_presentation_content_openai(prompt, api_key, progress_bar, progress_text):
84
+ try:
85
+ client = openai.OpenAI(api_key=api_key)
86
+
87
+ # Ustaw streamowanie dla postępu
88
+ response = client.chat.completions.create(
89
+ model="gpt-4o",
90
+ messages=[
91
+ {"role": "system", "content": "Jesteś ekspertem w tworzeniu prezentacji. Twórz treść prezentacji w formacie, gdzie każdy slajd zaczyna się od '#### [Tytuł slajdu]' a następnie zawiera treść slajdu. Używaj języka polskiego."},
92
+ {"role": "user", "content": prompt}
93
+ ],
94
+ stream=True,
95
+ max_tokens=4000
96
+ )
97
+
98
+ # Aktualizacja paska postępu i wyświetlanie generowanej treści
99
+ full_content = ""
100
+ for chunk in response:
101
+ if hasattr(chunk.choices[0].delta, 'content') and chunk.choices[0].delta.content:
102
+ content_chunk = chunk.choices[0].delta.content
103
+ full_content += content_chunk
104
+ progress_text.markdown(full_content)
105
+ time.sleep(0.01)
106
+ # Symulacja postępu - nie możemy dokładnie wiedzieć ile tekstu jeszcze zostanie wygenerowane
107
+ progress_bar.progress(min(len(full_content) / 3000, 1.0))
108
+
109
+ progress_bar.progress(1.0)
110
+ return full_content
111
+
112
+ except Exception as e:
113
+ logger.error(f"Błąd podczas generowania treści przez OpenAI: {e}")
114
+ st.error(f"Błąd podczas generowania treści przez OpenAI: {e}")
115
+ return None
116
+
117
+ # Funkcja do generowania treści prezentacji za pomocą DeepSeek
118
+ def generate_presentation_content_deepseek(prompt, api_key, progress_bar, progress_text):
119
+ try:
120
+ headers = {
121
+ "Content-Type": "application/json",
122
+ "Authorization": f"Bearer {api_key}"
123
+ }
124
+
125
+ data = {
126
+ "model": "deepseek-chat",
127
+ "messages": [
128
+ {"role": "system", "content": "Jesteś ekspertem w tworzeniu prezentacji. Twórz treść prezentacji w formacie, gdzie każdy slajd zaczyna się od '#### [Tytuł slajdu]' a następnie zawiera treść slajdu. Używaj języka polskiego."},
129
+ {"role": "user", "content": prompt}
130
+ ],
131
+ "stream": True,
132
+ "max_tokens": 4000
133
+ }
134
+
135
+ # Wykonanie żądania ze streamowaniem
136
+ response = requests.post(
137
+ "https://api.deepseek.com/chat/completions",
138
+ json=data,
139
+ headers=headers,
140
+ stream=True
141
+ )
142
+
143
+ if response.status_code != 200:
144
+ logger.error(f"Błąd DeepSeek API: {response.status_code} - {response.text}")
145
+ st.error(f"Błąd DeepSeek API: {response.status_code} - {response.text}")
146
+ return None
147
+
148
+ # Przetwarzanie streamowanej odpowiedzi
149
+ full_content = ""
150
+ for line in response.iter_lines():
151
+ if line:
152
+ line = line.decode('utf-8')
153
+ if line.startswith("data:") and not line.startswith("data: [DONE]"):
154
+ try:
155
+ json_str = line[5:].strip()
156
+ if json_str:
157
+ import json
158
+ data = json.loads(json_str)
159
+ if "choices" in data and len(data["choices"]) > 0:
160
+ choice = data["choices"][0]
161
+ if "delta" in choice and "content" in choice["delta"] and choice["delta"]["content"]:
162
+ content_chunk = choice["delta"]["content"]
163
+ full_content += content_chunk
164
+ progress_text.markdown(full_content)
165
+ # Symulacja postępu
166
+ progress_bar.progress(min(len(full_content) / 3000, 1.0))
167
+ except Exception as e:
168
+ logger.error(f"Błąd podczas parsowania odpowiedzi DeepSeek: {e}")
169
+
170
+ progress_bar.progress(1.0)
171
+ return full_content
172
+
173
+ except RequestException as e:
174
+ logger.error(f"Błąd połączenia z DeepSeek API: {e}")
175
+ st.error(f"Błąd połączenia z DeepSeek API: {e}")
176
+ return None
177
+ except Exception as e:
178
+ logger.error(f"Błąd podczas generowania treści przez DeepSeek: {e}")
179
+ st.error(f"Błąd podczas generowania treści przez DeepSeek: {e}")
180
+ return None
181
+
182
+ # Funkcja do pobierania pliku
183
+ def get_binary_file_downloader_html(file_path, file_label='File'):
184
+ with open(file_path, 'rb') as f:
185
+ data = f.read()
186
+ b64 = base64.b64encode(data).decode()
187
+ href = f'<a href="data:application/vnd.openxmlformats-officedocument.presentationml.presentation;base64,{b64}" download="{os.path.basename(file_path)}" class="download-button">📥 Pobierz prezentację PowerPoint</a>'
188
+ return href
189
+
190
+ # Główna funkcja aplikacji Streamlit
191
+ def main():
192
+ st.set_page_config(page_title="Generator Prezentacji LLM", layout="wide")
193
+
194
+ st.title("🎯 Generator Prezentacji LLM")
195
+ st.write("Ta aplikacja generuje prezentacje PowerPoint na dowolny temat za pomocą sztucznej inteligencji.")
196
+
197
+ # Wybór modelu
198
+ model = st.selectbox(
199
+ "Wybierz model LLM:",
200
+ ["GPT-4o", "DeepSeek"]
201
+ )
202
+
203
+ # Wybór formatu prezentacji
204
+ aspect_ratio = st.selectbox(
205
+ "Wybierz format prezentacji:",
206
+ ["16:9", "4:3"]
207
+ )
208
+
209
+ # Wprowadzenie klucza API
210
+ api_key = st.text_input("Wprowadź klucz API", type="password", help="Twój klucz API do wybranego modelu")
211
+
212
+ # Wprowadzenie tematu prezentacji
213
+ topic = st.text_area("Wprowadź temat prezentacji lub szczegółowe instrukcje:", height=150)
214
+
215
+ # Tworzenie tymczasowego katalogu na pliki
216
+ temp_dir = tempfile.mkdtemp()
217
+ output_file = os.path.join(temp_dir, "prezentacja.pptx")
218
+
219
+ if st.button("Generuj Prezentację", type="primary"):
220
+ if not api_key:
221
+ st.error("Proszę wprowadzić klucz API.")
222
+ return
223
+
224
+ if not topic:
225
+ st.error("Proszę wprowadzić temat prezentacji.")
226
+ return
227
+
228
+ # Przygotowanie promptu
229
+ prompt = f"""
230
+ Stwórz prezentację na temat: "{topic}".
231
+
232
+ Każdy slajd powinien zaczynać się od #### [Tytuł slajdu]
233
+ Po tytule powinien być umieszczony tekst slajdu.
234
+
235
+ Stwórz od 5 do 10 slajdów, które będą zawierać kompleksowe informacje o podanym temacie.
236
+ Rozpocznij od slajdu tytułowego, a zakończ slajdem podsumowującym.
237
+ """
238
+
239
+ st.write("### Generowanie treści prezentacji...")
240
+
241
+ # Pasek postępu i miejsce na wyświetlanie generowanego tekstu
242
+ progress_bar = st.progress(0)
243
+ progress_text = st.empty()
244
+
245
+ # Generowanie treści prezentacji w zależności od wybranego modelu
246
+ if model == "GPT-4o":
247
+ content = generate_presentation_content_openai(prompt, api_key, progress_bar, progress_text)
248
+ else: # DeepSeek
249
+ content = generate_presentation_content_deepseek(prompt, api_key, progress_bar, progress_text)
250
+
251
+ if content:
252
+ st.success("✅ Treść prezentacji została wygenerowana!")
253
+
254
+ # Podział na moduły (slajdy)
255
+ st.write("### Przetwarzanie slajdów...")
256
+ modules = split_into_modules(content)
257
+ st.info(f"Liczba wygenerowanych slajdów: {len(modules)}")
258
+
259
+ # Tworzenie prezentacji PowerPoint
260
+ with st.spinner("Tworzenie pliku PowerPoint..."):
261
+ create_ppt_from_modules(modules, output_file, aspect_ratio)
262
+
263
+ st.success(f"✅ Prezentacja PowerPoint została utworzona!")
264
+
265
+ # Przycisk do pobrania prezentacji
266
+ st.markdown(get_binary_file_downloader_html(output_file), unsafe_allow_html=True)
267
+
268
+ # Wyświetl treść wygenerowanej prezentacji
269
+ with st.expander("Zobacz wygenerowaną treść prezentacji"):
270
+ st.write(content)
271
+ else:
272
+ st.error("❌ Nie udało się wygenerować treści prezentacji. Sprawdź logi i klucz API.")
273
+
274
+ # Dodaj informacje o aplikacji
275
+ st.markdown("---")
276
+ st.markdown("### 📝 Informacje o aplikacji")
277
+ st.markdown("""
278
+ - Aplikacja używa AI do generowania treści prezentacji.
279
+ - Format wyjściowy: PowerPoint (.pptx).
280
+ - Wspierane modele: GPT-4o (OpenAI) i DeepSeek.
281
+ - Prezentacja będzie zawierać od 5 do 10 slajdów z kompleksowymi informacjami.
282
+ """)
283
+
284
+ # Dodaj informacje o użyciu API
285
+ st.markdown("### 🔑 Informacje o użyciu API")
286
+ st.markdown("""
287
+ - Dla GPT-4o: Użyj klucza API OpenAI ze swojego konta (format: sk-...).
288
+ - Dla DeepSeek: Użyj klucza API DeepSeek ze swojego konta.
289
+ - Twój klucz API jest używany tylko do generowania treści i nie jest nigdzie przechowywany.
290
+ """)
291
+
292
+ if __name__ ==