From 9690391e2625da8706ccbbbff3652a80d720eae7 Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 27 Sep 2023 21:43:54 +0800 Subject: [PATCH] Add Support for Manual pptx Modifications - Added a new feature that allows odin-slides adopt manual modifications done by the user to the pptx text. This includes manually modified text in the title or the content of the slides. Changes to be committed: modified: odin_slides/main.py modified: odin_slides/presentation.py modified: setup.py Fixes #1 --- odin_slides/main.py | 7 +++ odin_slides/presentation.py | 114 +++++++++++++++++++++++++++++------- setup.py | 11 +++- 3 files changed, 109 insertions(+), 23 deletions(-) diff --git a/odin_slides/main.py b/odin_slides/main.py index 7bd44cd..600c798 100644 --- a/odin_slides/main.py +++ b/odin_slides/main.py @@ -87,6 +87,13 @@ def main(): print(format_error("Something went wrong: ")+f"{e}"+"\n") except KeyboardInterrupt: print("\n"+format_info("Exiting...")) + print("Enjoying this project? Your support means a lot!") + print("If you find this open-source effort helpful, please consider giving it a star on GitHub.") + print("GitHub Repository: https://github.com/leonid20000/odin-slides") + print("Have ideas, found a bug, or need assistance?") + print("Feel free to create issues or submit pull requests on GitHub.") + print("\n"+format_info("Thank you for being part of the open-source community. See you next time!")) + finally: # Remember to close the log handler when done for handler in logger.handlers: diff --git a/odin_slides/presentation.py b/odin_slides/presentation.py index 490e700..34f302a 100644 --- a/odin_slides/presentation.py +++ b/odin_slides/presentation.py @@ -20,6 +20,10 @@ - build_slides_with_llm(template_file, word_content, output_file, session_file, logger): Build slides using a language model (LLM) based on user input. + - get_latest_slide_deck(pptx_file_path, current_slide_deck, logger): + Retrieves information from the latest PowerPoint slide deck in the specified file. + + Dependencies: - difflib - json @@ -145,7 +149,7 @@ def create_presentation(template_file, slide_content, output_file,logger): prs.save(output_file) break except PermissionError: - print(f"Action Required: The file '{output_file}' is open in PowerPoint or another application. Please close it and press Enter to retry.") + print(format_warning("Action Required:")+ f" The file {output_file} is open in PowerPoint or another application." +format_prompt("Please close it and press Enter to retry. ")) logger.error(f"Error: The file '{output_file}' is open in PowerPoint or another application. Please close it and press Enter to retry.") input() # Wait for the user to close the file except Exception as e: @@ -161,6 +165,62 @@ def create_presentation(template_file, slide_content, output_file,logger): logger.debug(f"Can not preview the presentation using os default viewer: {e}") +def get_latest_slide_deck(pptx_file_path, current_slide_deck, logger): + """ + Retrieves information from the latest PowerPoint slide deck in the specified file. + + Args: + pptx_file_path (str): The path to the PowerPoint file. + current_slide_deck (list): The current list of slide information. + logger (Logger): The logger object for error reporting. + + Returns: + list: A list of dictionaries containing slide information, including slide number, + title, content, and narration. + + Note: + This function attempts to load the PowerPoint presentation and extract slide + information. If the file is open in another application with a read lock (rare), it will prompt the user + to close it and retry. If the file does not exist, it returns the current slide deck. + + """ + presentation = None + while True: + try: + presentation = Presentation(pptx_file_path) + break + except PermissionError: + print(format_warning("Action Required:")+ f" The file {output_file} is open in PowerPoint or another application." +format_prompt("Please close it and press Enter to retry. ")) + logger.error(f"Error: The file '{output_file}' is open in PowerPoint or another application. Please close it and press Enter to retry.") + input() # Wait for the user to close the file + except FileNotFoundError: + return current_slide_deck + except Exception as e: + logger.error(f"An unexpected error occurred while loading the presentation: {e}") + return current_slide_deck + + updated_slide_deck = [] + + for slide_number, slide in enumerate(presentation.slides, start=1): + slide_info = {"slide_number": float(slide_number), "title": "", "content": "", "narration": ""} + + for shape in slide.shapes: + if shape.has_text_frame: + if shape == slide.shapes[0]: # Assuming the first shape is the title + slide_info["title"] = shape.text + else: + slide_info["content"] += shape.text + "\n" + # Extract notes from the notes slide + notes_slide = slide.notes_slide + for shape in notes_slide.shapes: + if shape.has_text_frame: + slide_info["narration"] += shape.text + "\n" + + updated_slide_deck.append(slide_info) + + return updated_slide_deck + + def build_slides_with_llm(template_file,word_content, output_file, session_file, logger): """Build slides using a language model (LLM) based on user input. @@ -175,23 +235,30 @@ def build_slides_with_llm(template_file,word_content, output_file, session_file, slide_deck_history=[[]] try: if not session_file: - # Ask the user for prompt - prompt = input(format_prompt("\nWhat slides shall I make for you? ")) - print(format_info("OK, please wait. This might take a while...")+"\n") - slide_deck=get_chat_response(word_content,[], prompt,logger) - logger.debug(slide_deck) - while True: - try: - slide_deck=ensure_list(json.loads(slide_deck)) - slide_deck_history=[slide_deck] - break - except Exception as e: - logger.debug("Not neccessarily an error but Invalid JSON string") - logger.debug(e) - # Ask the user for prompt - prompt = input(format_prompt("Hmm, not sure what you want so I did not make any changes. Try differently: ")) - print(format_info("OK, please wait. This might take a while...")+"\n") - slide_deck=get_chat_response(word_content,[], prompt,logger) + initial_slide_deck = get_latest_slide_deck(output_file, [], logger) + if initial_slide_deck != []: + print(format_warning("The output file is not empty but no session file is supplied. If you have the session file, consider continuing from the previously saved session.")+"\n") + print(format_info("Loading the existing text content of the file into a new session...")+"\n") + slide_deck = initial_slide_deck + slide_deck_history=[slide_deck] + else: + # Ask the user for prompt + prompt = input(format_prompt("\nWhat shall I do for you? ")) + print(format_info("OK, please wait. This might take a while...")+"\n") + slide_deck=get_chat_response(word_content, initial_slide_deck, prompt,logger) + logger.debug(slide_deck) + while True: + try: + slide_deck=ensure_list(json.loads(slide_deck)) + slide_deck_history=[slide_deck] + break + except Exception as e: + logger.debug("Not neccessarily an error but Invalid JSON string") + logger.debug(e) + # Ask the user for prompt + prompt = input(format_prompt("Hmm, not sure what you want so I did not make any changes. Try differently: ")) + print(format_info("OK, please wait. This might take a while...")+"\n") + slide_deck=get_chat_response(word_content,[], prompt,logger) @@ -204,7 +271,8 @@ def build_slides_with_llm(template_file,word_content, output_file, session_file, logger.debug(loaded_data) # Access slide_deck_history variable from the loaded data slide_deck_history = loaded_data['slide_deck_history'] - slide_deck=slide_deck_history[-1] + slide_deck = get_latest_slide_deck(output_file, slide_deck_history[-1], logger) + slide_deck_history[-1] = slide_deck logger.debug(slide_deck) while True: @@ -216,6 +284,7 @@ def build_slides_with_llm(template_file,word_content, output_file, session_file, slide_deck_history.pop() if len(slide_deck_history) > 1 else None slide_deck = slide_deck_history[-1] continue + slide_deck = get_latest_slide_deck(output_file, slide_deck, logger) result=get_chat_response(word_content,slide_deck, prompt,logger) try: result=ensure_list(json.loads(result)) @@ -265,9 +334,12 @@ def build_slides_with_llm(template_file,word_content, output_file, session_file, logger.error("Something went wrong while making presentation:") logger.error(e) finally: - print("\nSaving the session to "+output_file.replace(".", "_")+'_session.pkl') + file_name=output_file.replace(".", "_")+'_session.pkl' + if os.path.exists(file_name) and not session_file: + file_name = file_name.replace(".pkl", f"_new.pkl") + print("\nSaving the session to "+file_name) # Saving variables to a file - with open(output_file.replace(".", "_")+'_session.pkl', 'wb') as file: + with open(file_name, 'wb') as file: session_data = { 'slide_deck_history': slide_deck_history, 'word_content': word_content diff --git a/setup.py b/setup.py index 3e56468..12286b4 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="odin-slides", - version="0.5", + version="0.6", packages=find_packages(), install_requires=[ 'python-pptx', @@ -43,7 +43,14 @@ 6. **Extensibility:** odin-slides is designed for extensibility, accommodating additional Language Models and file types in forthcoming updates. Anticipate enhanced functionality and new features as the tool evolves. -Stay ahead in the world of presentations with odin-slides — your versatile and intelligent partner in creating impactful content. +### Latest Updates + +#### Version 0.6 (September 27, 2023) + +- Added a new feature that allows odin-slides adopt manual modifications done by the user to the pptx text. This includes manually modified text in the title or the content of the slides. ([Issue #1](https://github.com/leonid20000/odin-slides/issues/1)) + + +Stay ahead in the world of presentations with odin-slides — your versatile and intelligent helper in creating impactful content. """, description="An advanced Python tool that empowers you to effortlessly draft impressive PowerPoint presentations from Word documents using generative AI.", url="https://github.com/leonid20000/odin-slides",