Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import os | |
| import time | |
| import logging | |
| import tempfile | |
| from pptx import Presentation | |
| from pptx.util import Inches | |
| from pptx.dml.color import RGBColor | |
| from pptx.enum.text import PP_ALIGN | |
| import openai | |
| import requests | |
| from requests.exceptions import RequestException | |
| import base64 | |
| # Konfiguracja logowania | |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s') | |
| logger = logging.getLogger(__name__) | |
| # Funkcja do usuwania formatowania Markdown | |
| def remove_markdown_formatting(text): | |
| return text.replace('**', '').replace('#', '').strip() | |
| # Funkcja do podziału tekstu na moduły (slajdy) | |
| def split_into_modules(content): | |
| modules = content.split('####') | |
| modules = [module.strip() for module in modules if module.strip()] | |
| # Logowanie modułów do debugowania | |
| for i, module in enumerate(modules): | |
| logger.info(f"Moduł {i + 1}: {module[:100]}...") # Logowanie pierwszych 100 znaków modułu | |
| return modules | |
| # Funkcja do tworzenia prezentacji z modułów | |
| def create_ppt_from_modules(modules, output_file, aspect_ratio="16:9"): | |
| prs = Presentation() | |
| # Ustawienie formatu slajdów | |
| if aspect_ratio == "4:3": | |
| prs.slide_width = Inches(10) | |
| prs.slide_height = Inches(7.5) | |
| else: # 16:9 | |
| prs.slide_width = Inches(13.33) | |
| prs.slide_height = Inches(7.5) | |
| for module in modules: | |
| try: | |
| slide = prs.slides.add_slide(prs.slide_layouts[1]) | |
| title, *body = module.split('\n', 1) | |
| # Formatowanie tytułu slajdu | |
| title_shape = slide.shapes.title | |
| title_shape.text = remove_markdown_formatting(title) | |
| title_paragraph = title_shape.text_frame.paragraphs[0] | |
| title_paragraph.font.size = Inches(0.5) # Rozmiar tytułu | |
| title_paragraph.font.name = 'Helvetica' | |
| title_paragraph.alignment = PP_ALIGN.LEFT | |
| # Formatowanie treści slajdu | |
| if body: | |
| content = remove_markdown_formatting(body[0].strip()) | |
| text_frame = slide.placeholders[1].text_frame | |
| text_frame.text = content | |
| for paragraph in text_frame.paragraphs: | |
| if paragraph.text.strip(): # Sprawdź, czy akapit ma tekst | |
| paragraph.font.size = Inches(0.25) # Rozmiar tekstu | |
| paragraph.font.name = 'Helvetica' | |
| paragraph.alignment = PP_ALIGN.LEFT | |
| else: | |
| logger.warning(f"Pusty akapit w module: {title}") | |
| else: | |
| logger.warning(f"Brak treści w module: {title}") | |
| except Exception as e: | |
| logger.error(f"Błąd podczas przetwarzania modułu: {module}. Błąd: {e}") | |
| st.error(f"Błąd podczas przetwarzania modułu: {module}. Błąd: {e}") | |
| prs.save(output_file) | |
| logger.info(f"Prezentacja została zapisana jako {output_file}") | |
| # Funkcja do generowania treści prezentacji za pomocą OpenAI | |
| def generate_presentation_content_openai(prompt, api_key, progress_bar, progress_text): | |
| try: | |
| headers = { | |
| "Content-Type": "application/json", | |
| "Authorization": f"Bearer {api_key}" | |
| } | |
| data = { | |
| "model": "gpt-4o", | |
| "messages": [ | |
| {"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." | |
| "Nie dodawaj jednak frazy 'Tytuł slajdu:' tylko od razu wstaw adekwatny tytuł. Po tytule umieść treść slajdu."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| "stream": True, | |
| "max_tokens": 8000 | |
| } | |
| # Wykonanie żądania ze streamowaniem | |
| response = requests.post( | |
| "https://api.openai.com/v1/chat/completions", | |
| json=data, | |
| headers=headers, | |
| stream=True, | |
| # Możemy dodać proxy, jeśli jest to wymagane | |
| proxies={ | |
| "http": os.getenv("HTTP_PROXY", ""), | |
| "https": os.getenv("HTTPS_PROXY", "") | |
| } | |
| ) | |
| if response.status_code != 200: | |
| logger.error(f"Błąd OpenAI API: {response.status_code} - {response.text}") | |
| st.error(f"Błąd OpenAI API: {response.status_code} - {response.text}") | |
| return None | |
| # Przetwarzanie streamowanej odpowiedzi | |
| full_content = "" | |
| for line in response.iter_lines(): | |
| if line: | |
| line = line.decode('utf-8') | |
| if line.startswith("data:") and not line.startswith("data: [DONE]"): | |
| try: | |
| json_str = line[5:].strip() | |
| if json_str: | |
| import json | |
| data = json.loads(json_str) | |
| if "choices" in data and len(data["choices"]) > 0: | |
| choice = data["choices"][0] | |
| if "delta" in choice and "content" in choice["delta"] and choice["delta"]["content"]: | |
| content_chunk = choice["delta"]["content"] | |
| full_content += content_chunk | |
| progress_text.markdown(full_content) | |
| # Symulacja postępu | |
| progress_bar.progress(min(len(full_content) / 3000, 1.0)) | |
| except Exception as e: | |
| logger.error(f"Błąd podczas parsowania odpowiedzi OpenAI: {e}") | |
| progress_bar.progress(1.0) | |
| return full_content | |
| except RequestException as e: | |
| logger.error(f"Błąd połączenia z OpenAI API: {e}") | |
| st.error(f"Błąd połączenia z OpenAI API: {e}") | |
| return None | |
| except Exception as e: | |
| logger.error(f"Błąd podczas generowania treści przez OpenAI: {e}") | |
| st.error(f"Błąd podczas generowania treści przez OpenAI: {e}") | |
| return None | |
| # Funkcja do generowania treści prezentacji za pomocą DeepSeek | |
| def generate_presentation_content_deepseek(prompt, api_key, progress_bar, progress_text): | |
| try: | |
| headers = { | |
| "Content-Type": "application/json", | |
| "Authorization": f"Bearer {api_key}" | |
| } | |
| data = { | |
| "model": "deepseek-chat", | |
| "messages": [ | |
| {"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." | |
| "Nie dodawaj jednak frazy 'Tytuł slajdu:' tylko od razu wstaw adekwatny tytuł. Po tytule umieść treść slajdu."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| "stream": True, | |
| "max_tokens": 8000 | |
| } | |
| # Wykonanie żądania ze streamowaniem | |
| response = requests.post( | |
| "https://api.deepseek.com/chat/completions", | |
| json=data, | |
| headers=headers, | |
| stream=True | |
| ) | |
| if response.status_code != 200: | |
| logger.error(f"Błąd DeepSeek API: {response.status_code} - {response.text}") | |
| st.error(f"Błąd DeepSeek API: {response.status_code} - {response.text}") | |
| return None | |
| # Przetwarzanie streamowanej odpowiedzi | |
| full_content = "" | |
| for line in response.iter_lines(): | |
| if line: | |
| line = line.decode('utf-8') | |
| if line.startswith("data:") and not line.startswith("data: [DONE]"): | |
| try: | |
| json_str = line[5:].strip() | |
| if json_str: | |
| import json | |
| data = json.loads(json_str) | |
| if "choices" in data and len(data["choices"]) > 0: | |
| choice = data["choices"][0] | |
| if "delta" in choice and "content" in choice["delta"] and choice["delta"]["content"]: | |
| content_chunk = choice["delta"]["content"] | |
| full_content += content_chunk | |
| progress_text.markdown(full_content) | |
| # Symulacja postępu | |
| progress_bar.progress(min(len(full_content) / 3000, 1.0)) | |
| except Exception as e: | |
| logger.error(f"Błąd podczas parsowania odpowiedzi DeepSeek: {e}") | |
| progress_bar.progress(1.0) | |
| return full_content | |
| except RequestException as e: | |
| logger.error(f"Błąd połączenia z DeepSeek API: {e}") | |
| st.error(f"Błąd połączenia z DeepSeek API: {e}") | |
| return None | |
| except Exception as e: | |
| logger.error(f"Błąd podczas generowania treści przez DeepSeek: {e}") | |
| st.error(f"Błąd podczas generowania treści przez DeepSeek: {e}") | |
| return None | |
| # Funkcja do pobierania pliku | |
| def get_binary_file_downloader_html(file_path, file_label='File'): | |
| with open(file_path, 'rb') as f: | |
| data = f.read() | |
| b64 = base64.b64encode(data).decode() | |
| 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>' | |
| return href | |
| # Główna funkcja aplikacji Streamlit | |
| def main(): | |
| st.set_page_config(page_title="Generator Prezentacji PPT", layout="wide") | |
| st.title("🎯 Generator Prezentacji PPT") | |
| st.write("Ta aplikacja generuje prezentacje PowerPoint na dowolny temat za pomocą sztucznej inteligencji.") | |
| # Wybór modelu | |
| model = st.selectbox( | |
| "Wybierz model LLM:", | |
| ["GPT-4o", "DeepSeek"] | |
| ) | |
| # Wybór formatu prezentacji | |
| aspect_ratio = st.selectbox( | |
| "Wybierz format prezentacji:", | |
| ["16:9", "4:3"] | |
| ) | |
| # Wprowadzenie klucza API | |
| api_key = st.text_input("Wprowadź klucz API", type="password", help="Twój klucz API do wybranego modelu") | |
| # Wprowadzenie tematu prezentacji | |
| topic = st.text_area("Wprowadź temat prezentacji lub szczegółowe instrukcje:", height=150) | |
| # Tworzenie tymczasowego katalogu na pliki | |
| temp_dir = tempfile.mkdtemp() | |
| output_file = os.path.join(temp_dir, "prezentacja.pptx") | |
| if st.button("Generuj Prezentację", type="primary"): | |
| if not api_key: | |
| st.error("Proszę wprowadzić klucz API.") | |
| return | |
| if not topic: | |
| st.error("Proszę wprowadzić temat prezentacji.") | |
| return | |
| # Przygotowanie promptu | |
| prompt = f""" | |
| Stwórz prezentację na temat: "{topic}". | |
| Każdy slajd powinien zaczynać się od #### Tytuł slajdu | |
| Po tytule powinien być umieszczony tekst slajdu. | |
| Stwórz slajdy, które będą zawierać kompleksowe informacje o podanym temacie. | |
| Rozpocznij od slajdu tytułowego, a zakończ slajdem podsumowującym. | |
| """ | |
| st.write("### Generowanie treści prezentacji...") | |
| # Pasek postępu i miejsce na wyświetlanie generowanego tekstu | |
| progress_bar = st.progress(0) | |
| progress_text = st.empty() | |
| # Generowanie treści prezentacji w zależności od wybranego modelu | |
| if model == "GPT-4o": | |
| content = generate_presentation_content_openai(prompt, api_key, progress_bar, progress_text) | |
| else: # DeepSeek | |
| content = generate_presentation_content_deepseek(prompt, api_key, progress_bar, progress_text) | |
| content = content.replace("\r", "").replace("\n\n---\n\n", "\n\n").replace("\n---\n", "\n").replace("---", "") # usuwanie zbędnych separatorów | |
| if content: | |
| st.success("✅ Treść prezentacji została wygenerowana!") | |
| # Podział na moduły (slajdy) | |
| st.write("### Przetwarzanie slajdów...") | |
| modules = split_into_modules(content) | |
| st.info(f"Liczba wygenerowanych slajdów: {len(modules)}") | |
| # Tworzenie prezentacji PowerPoint | |
| with st.spinner("Tworzenie pliku PowerPoint..."): | |
| create_ppt_from_modules(modules, output_file, aspect_ratio) | |
| st.success(f"✅ Prezentacja PowerPoint została utworzona!") | |
| # Przycisk do pobrania prezentacji | |
| st.markdown(get_binary_file_downloader_html(output_file), unsafe_allow_html=True) | |
| # Wyświetl treść wygenerowanej prezentacji | |
| with st.expander("Zobacz wygenerowaną treść prezentacji"): | |
| st.write(content) | |
| else: | |
| st.error("❌ Nie udało się wygenerować treści prezentacji. Sprawdź logi i klucz API.") | |
| # Dodaj informacje o aplikacji | |
| st.markdown("---") | |
| st.markdown("### 📝 Informacje o aplikacji") | |
| st.markdown(""" | |
| - Aplikacja używa AI do generowania treści prezentacji. | |
| - Format wyjściowy: PowerPoint (.pptx). | |
| - Wspierane modele: GPT-4o (OpenAI) i DeepSeek. | |
| """) | |
| # Dodaj informacje o użyciu API | |
| st.markdown("### 🔑 Informacje o użyciu API") | |
| st.markdown(""" | |
| - Dla GPT-4o: Użyj klucza API OpenAI ze swojego konta: https://platform.openai.com/api-keys | |
| - Dla DeepSeek: Użyj klucza API DeepSeek ze swojego konta: https://platform.deepseek.com/api_keys | |
| - Twój klucz API jest używany tylko do generowania treści i nie jest nigdzie przechowywany. | |
| """) | |
| if __name__ == "__main__": | |
| try: | |
| main() | |
| except Exception as e: | |
| st.error(f"Wystąpił nieoczekiwany błąd: {str(e)}") | |
| logger.exception("Nieoczekiwany błąd aplikacji:") |