Spaces:
Paused
Paused
import json | |
import time | |
import streamlit as st | |
import streamlit.runtime.scriptrunner as st_sr | |
import llm_helper | |
import pptx_helper | |
from global_config import GlobalConfig | |
UI_BUTTONS = [ | |
'Generate slides content', | |
'Generate JSON', | |
'Make the slides' | |
] | |
def build_ui(): | |
""" | |
Display the input elements for content generation. Only covers the first step. | |
""" | |
st.title('Slides Wizard') | |
st.subheader('*:blue[Create your next PowerPoint slide deck using AI]*') | |
st.divider() | |
st.header('Step 1: Generate your content') | |
st.caption('Let\'s start by generating some contents for your slides') | |
# name = st.text_input( | |
# f'''**Type in your name**''', | |
# value='John Doe' | |
# ) | |
try: | |
with open(GlobalConfig.PRELOAD_DATA_FILE, 'r') as in_file: | |
preload_data = json.loads(in_file.read()) | |
except (FileExistsError, FileNotFoundError): | |
preload_data = {'topic': '', 'audience': ''} | |
topic = st.text_area( | |
f'''**Describe the topic of the presentation. | |
Avoid mentioning the count of slides.**''', | |
value=preload_data['topic'] | |
) | |
audience = st.text_input( | |
f'''**Briefly describe your target audience**''', | |
value=preload_data['audience'] | |
) | |
# Button with callback function | |
st.button(UI_BUTTONS[0], on_click=button_clicked, args=[0]) | |
if st.session_state.clicked[0]: | |
progress_text = 'Generating your presentation slides...give it a moment' | |
progress_bar = st.progress(0, text=progress_text) | |
# name_txt = name.strip() | |
topic_txt = topic.strip() | |
audience_txt = audience.strip() | |
process_topic_inputs('', topic_txt, audience_txt, progress_bar) | |
def process_topic_inputs(name: str, topic: str, audience: str, progress_bar): | |
""" | |
Process the inputs to generate contents for the slides. | |
:param name: Name of the speaker | |
:param topic: The presentation topic | |
:param audience: Target audience description | |
:param progress_bar: Progress bar from the page | |
:return: | |
""" | |
# name_length = len(name) | |
topic_length = len(topic) | |
audience_length = len(audience) | |
print(f'Input lengths:: topic: {topic_length}, audience: {audience_length}') | |
if topic_length > 10 and audience_length > 5: | |
print( | |
f'Name: {name}\n' | |
f'Topic: {topic}\n' | |
f'Audience: {audience}' | |
) | |
print('=' * 20) | |
target_length = min(topic_length, GlobalConfig.LLM_MODEL_MAX_INPUT_LENGTH) | |
try: | |
slides_content = llm_helper.generate_slides_content(name, topic[:target_length], audience) | |
print('=' * 20) | |
print(f'Slides content:\n{slides_content}') | |
print('=' * 20) | |
st.write(f'''Slides content:\n{slides_content}''') | |
progress_bar.progress(100, text='Done!') | |
st.info( | |
'The generated content doesn\'t look so great?' | |
' Need alternatives? Just change your description text and try again.' | |
' For example, you can start describing like "Create a slide deck on..."', | |
icon="ℹ️" | |
) | |
# Move on to step 2 | |
st.divider() | |
st.header('Step 2: Make it structured') | |
st.caption('Let\'s now convert the above generated contents into JSON') | |
# Streamlit multiple buttons work in a weird way! | |
# Click on any button, the page just reloads! | |
# Buttons are not "stateful" | |
# https://blog.streamlit.io/10-most-common-explanations-on-the-streamlit-forum/#1-buttons-aren%E2%80%99t-stateful | |
# Apparently, "nested button click" needs to be handled differently | |
# https://playground.streamlit.app/?q=triple-button | |
st.button(UI_BUTTONS[1], on_click=button_clicked, args=[1]) | |
if st.session_state.clicked[1]: | |
progress_text = 'Converting...give it a moment' | |
progress_bar = st.progress(0, text=progress_text) | |
process_slides_contents(slides_content, progress_bar) | |
except ValueError as ve: | |
st.error(f'Unfortunately, an error occurred: {ve}! ' | |
f'Please change the text, try again later, or report it, sharing your inputs.') | |
else: | |
st.error('Not enough information provided! Please be little more descriptive :)') | |
def process_slides_contents(text: str, progress_bar: st.progress): | |
""" | |
Convert given content to JSON and display. Update the UI. | |
:param text: The contents generated for the slides | |
:param progress_bar: Progress bar for this step | |
""" | |
print('JSON button clicked') | |
json_str = llm_helper.text_to_json(text) | |
print('=' * 20) | |
print(f'JSON:\n{json_str}') | |
print('=' * 20) | |
st.code(json_str, language='json') | |
progress_bar.progress(100, text='Done!') | |
# Now, step 3 | |
st.divider() | |
st.header('Step 3: Create the slides') | |
st.caption('Let\'s now create the slides for you') | |
st.button(UI_BUTTONS[2], on_click=button_clicked, args=[2]) | |
if st.session_state.clicked[2]: | |
progress_text = 'Creating...give it a moment' | |
progress_bar = st.progress(0, text=progress_text) | |
# Get a unique name for the file to save -- use the session ID | |
ctx = st_sr.get_script_run_ctx() | |
session_id = ctx.session_id | |
timestamp = time.time() | |
output_file_name = f'{session_id}_{timestamp}.pptx' | |
pptx_helper.generate_powerpoint_presentation(json_str, output_file_name) | |
st.progress(100, text='Done!') | |
# st.download_button('Download file', binary_contents) # Defaults to 'application/octet-stream' | |
with open(output_file_name, 'rb') as f: | |
st.download_button('Download PPTX file', f, file_name=output_file_name) | |
def button_clicked(button): | |
""" | |
Update the button clicked value in session state. | |
""" | |
st.session_state.clicked[button] = True | |
def main(): | |
# Initialize the key in session state to manage the nested buttons states | |
if 'clicked' not in st.session_state: | |
st.session_state.clicked = {0: False, 1: False, 2: False} | |
build_ui() | |
if __name__ == '__main__': | |
main() | |