diff --git a/powerpoint_generative_ai/domain/prompts.py b/powerpoint_generative_ai/domain/prompts.py index 02d9893..3a4ca7d 100644 --- a/powerpoint_generative_ai/domain/prompts.py +++ b/powerpoint_generative_ai/domain/prompts.py @@ -1,3 +1,85 @@ +TOOL_USE_PROMPT = """ +YOU MUST ALWAYS OUTPUT IN THE GIVEN FORMAT. EVEN IF OTHER OUTPUTS ARE DIFFERENT. +You are an analyst and a masterful tool user. Your job right now is to determine whether to call a tool or not call a tool. + +You must analyze the given context. + +You can use the following tools: + +- generate_chart(param): Identify if the user has passed data for a chart (different from a diagram). If user wants a specific chart, give him that. If not, give him the best chart for the data. The param should include chart type too. + +Only return the value of the most applicable chart type: + +{ + "BAR_CLUSTERED": {"value": 57, "description": "Clustered Bar."}, + "BAR_OF_PIE": {"value": 71, "description": "Bar of Pie."}, + "BAR_STACKED": {"value": 58, "description": "Stacked Bar."}, + "BAR_STACKED_100": {"value": 59, "description": "100% Stacked Bar."}, + "COLUMN_CLUSTERED": {"value": 51, "description": "Clustered Column."}, + "COLUMN_STACKED": {"value": 52, "description": "Stacked Column."}, + "COLUMN_STACKED_100": {"value": 53, "description": "100% Stacked Column."}, + "LINE": {"value": 4, "description": "Line."}, + "LINE_MARKERS": {"value": 65, "description": "Line with Markers."}, + "LINE_MARKERS_STACKED": {"value": 66, "description": "Stacked Line with Markers."}, + "LINE_MARKERS_STACKED_100": {"value": 67, "description": "100% Stacked Line with Markers."}, + "LINE_STACKED": {"value": 63, "description": "Stacked Line."}, + "LINE_STACKED_100": {"value": 64, "description": "100% Stacked Line."} +} + +Again, only return the value of the most applicable chart type. So for line chart you would use 4, etc. + +- generate_mermaid_diagram(param): in here you can pass mermaid syntax text to generate a diagram (different from a chart). If user wants a diagram, give him one using this. + +Pass the mermaid syntax text AND descriptive name of the diagram in the param. Analyze what the user wants, then convert it into a proper diagram. Then pass the diagram to the function. +param will look like: "" @,@ "Diagram name" + +User does not need to pass any data for diagrams, make the diagram on your own. Unless user has passed some data for a diagram, then use that data to make the diagram. + +You can make sophisticated diagrams and simple ones too. Try to explain the topics properly. + +Yes, use @,@ to separate the mermaid syntax text and the name of the diagram. + + +==== + +All your outputs have to be in this format: + + +Think before your actual output, think: +- What is the user asking? +- Should we use a tool here? +- How should we use the tool here? +- Do we need a chart or a diagram? +---- How can we generate a creative diagram? Make sure to not just copy paste the format of the example. +- Analyze the data. + +Plan ahead here. + +DO NOT USE MORE THAN 5-7 sentences TO THINK. + + + +IF you do not want to call a function, output- call:none:none + +IF you want to call a function, output in this format: +call<|?|>func_name<|?|>param +example - call<|?|>generate_chart<|?|>51 +example - call<|?|>generate_mermaid_diagram<|?|>graph TD; A-->B; A-->C; B-->D; C-->D;@,@Diagram name + +Remember that these are just examples. Make your own diagrams and charts. Do not be limited to these. + +Understand that you can call multiple functions at the same time. Every function call must be in a seperate line. + +If you do not want to call a function, output- call<|?|>none<|?|>none + + + +===== +YOU CAN ONLY DO ONE THING, either generate an output, or call a function. But always output in this given format. The output will be used in our program, so it has to be in this format. Otherwise the code will break. +Do not hallucinate. +""" + + DECK_CREATION_SYSTEM_PROMPT = """Take the user input and create content for a slideshow related to the user's input. You will generate titles for the slides, content that tells a cohesive story throughout the slides. DO NOT title each slide like 'Slide X: ...'. Data may be provided in the input, if it has been provided determine the best slide to include a chart. ONLY INSERT CHARTS when data is provided. @@ -32,9 +114,14 @@ }, { 'title': 'Slide 3', - 'content': 'This is some content for slide 3' + 'content': 'This is some detailed content for slide 3 which goes well with the diagram.', + 'diagram_name': 'Diagram name' } ] +====== + +Note that the text you generate should be detailed and user should always learn something new. But do not write too much, short sentences with good information. + Note: Your output must be parsable, valid JSON. DO NOT summarize what each slide was about, the content on each slide should be meaningful information""" @@ -62,4 +149,74 @@ TITLE_GEN_SYSTEM_PROMPT = """Generate a title for this powerpoint based on the content""" -FILENAME_SYSTEM_PROMPT = """Take the powerpoint title in the user text and create a short version to be used as a filename for a .pptx file""" \ No newline at end of file +FILENAME_SYSTEM_PROMPT = """Take the powerpoint title in the user text and create a short version to be used as a filename for a .pptx file""" + + +SLIDE_CREATION_PROMPT = """ +Take the user input and create content for a slide in a slide show. You are given description of a single slide. You will generate a title for the slide, content that tells a cohesive story throughout the slide. DO NOT title the slide like 'Slide X: ...'. +Make sure the content you write is informative and extensive. But it doesn't need to be too long. Short sentences with good information is the key. + +You have to always output in a very specific format, here are some examples: + +example 1: +{ + "title": "Slide 1", + "content": "This is some content for slide 1" +} + +example 2: + +{ + "title": "Slide 1", + "content": "This slide has a multi-line chart", + "chart_data": { + "categories": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"], + "series": [ + {"name": "Series 1", "values": [1, 2, 3, 4, 5, 6, 7]}, + {"name": "Series 2", "values": [2, 3, 4, 5, 6, 7, 8]}, + {"name": "Series 3", "values": [3, 4, 5, 6, 7, 8, 9]} + ] + }, + "chart_type": 4 +} + +example 3: + + { + "title": "Slide 1", + "content": "This is some detailed content for slide 1. Informative.", + "diagram_name": "Diagram name" + } + +example 4: + + { + "title": "Slide 1", + "content": "This is some detailed content for slide 1. Informative and detailed.", + "table_data": "Table data" // NOTE: This must be CSV data properly formatted. Must be parsable. + } + + + +VERY IMPORTANT: CSV DATA MUST HAVE SAME NUMBER OF COLUMNS IN EACH ROW. IF NOT, THE CODE WILL BREAK. The CSV must be parsable. Format the data properly. + +You will be provided diagram_name and chart_data and table_data when necessary. +If you see some csv data, generate a table. But if user has provided some data for a chart, use that data to generate a chart. If user asked for a chart, then generate a chart. + +Note that you are an expert, you must write excellent content. You must always output in the given format. Even if other outputs are different. +Be engaging and always provide new information. Do not summarize what the slide is about, the content on each slide should be meaningful information. + +But write short sentences with good information. Do not write too much. + +DO NOT SAY THINGS LIKE "This slide has" or "This slide contains", just write the content. +Be informative, no need to mention what the slide contains, just write directly. + +Note: Your output must be parsable, valid JSON. ALWAYS OUTPUT VALID JSON. + +GENERATE PROPER CSV DATA FOR TABLES when required. +Again, if you see csv data, generate a table. + +THERE CAN ONLY EITHER BE A CHART OR A DIAGRAM OR A TABLE. NOT MULTIPLE, ONLY ONE OF THEM. FOLLOW THE FORMAT. +Given CSV data, generate a table. + +""" \ No newline at end of file diff --git a/powerpoint_generative_ai/ppt/ppt_creator.py b/powerpoint_generative_ai/ppt/ppt_creator.py index 52008d4..7ec6cf1 100644 --- a/powerpoint_generative_ai/ppt/ppt_creator.py +++ b/powerpoint_generative_ai/ppt/ppt_creator.py @@ -3,7 +3,7 @@ from pptx.slide import Slide from pptx.chart.data import CategoryChartData from pptx.enum.chart import XL_CHART_TYPE -from pptx.util import Inches +from pptx.util import Inches, Pt from powerpoint_generative_ai.utils.utils import setup_logger @@ -46,12 +46,17 @@ def add_slide(self, content: dict): """Helper function to add slides to powerpoint""" text_content = content.get('content', None) chart_data = content.get('chart_data', None) + image_path = content.get('diagram_name', None) + table_data = content.get('table_data', None) + LAYOUT = SLIDE_LAYOUTS['Title Slide'] - if chart_data and text_content: + if table_data: + LAYOUT = SLIDE_LAYOUTS['Title Only'] + elif text_content and (chart_data or image_path): LAYOUT = SLIDE_LAYOUTS['Two Content'] # Text column and blank right side elif chart_data and not text_content: LAYOUT = SLIDE_LAYOUTS['Title Only'] # Just a title for a big chart - elif text_content and not chart_data: + elif (text_content and not chart_data): LAYOUT = SLIDE_LAYOUTS['Title and Content'] # Standard slide format slide_layout = self.presentation.slide_layouts[LAYOUT] @@ -59,6 +64,13 @@ def add_slide(self, content: dict): title = slide.shapes.title title.text = content.get('title', None) + + if table_data: + title.text = text_content + title.text_frame.paragraphs[0].font.size = Pt(13) + self.add_table(slide=slide, csv_text=table_data) + return + if text_content: content_shape = slide.shapes.placeholders[1] content_shape.text = text_content @@ -66,6 +78,10 @@ def add_slide(self, content: dict): if chart_data: self.add_chart(data=chart_data, slide=slide, chart_type=content.get('chart_type', XL_CHART_TYPE.COLUMN_CLUSTERED)) + if image_path: + self.add_image(image_path=image_path, slide=slide) + + def add_chart(self, data: dict, slide: Slide, x: Inches = Inches(4.75), y: Inches = Inches(2), cx: Inches=Inches(5.5), cy: Inches = Inches(4.5), chart_type: int = XL_CHART_TYPE.COLUMN_CLUSTERED): """Creates a chart and adds it to the current slide""" @@ -75,12 +91,40 @@ def add_chart(self, data: dict, slide: Slide, x: Inches = Inches(4.75), y: Inche chart_data.add_series(series['name'], series['values']) chart = slide.shapes.add_chart(chart_type, x, y, cx, cy, chart_data).chart + chart.has_legend = True for series in chart.series: series.has_data_labels = True + def add_image(self, image_path: str, slide: Slide, x: Inches = Inches(4.75), y: Inches = Inches(2), cx: Inches=Inches(4), cy: Inches = Inches(3.5)): + """Adds an image to the current slide""" + slide.shapes.add_picture(image_path, x, y, cx, cy) + + def add_table(self, slide: Slide, csv_text: str): + """Adds a table to the current slide""" + + shapes = slide.shapes + self._csv_to_table(shapes, csv_text) def save(self, file_name: str): self.presentation.save(file_name) self.logger.info(f"Presentation successfully created: {file_name}") + def _csv_to_table(self, shapes, csv_text): + rows = csv_text.split('\n') + data = [row.split(',') for row in rows] + + row_count = len(data) + col_count = len(data[0]) + + left = top = Inches(2.0) + width = Inches(6.0) + height = Inches(0.8) + + + table = shapes.add_table(row_count, col_count, left, top, width, height).table + + for r in range(row_count): + for c in range(col_count): + cell = table.cell(r, c) + cell.text = data[r][c] diff --git a/powerpoint_generative_ai/ppt_generator.py b/powerpoint_generative_ai/ppt_generator.py index 5bb7fde..126c837 100644 --- a/powerpoint_generative_ai/ppt_generator.py +++ b/powerpoint_generative_ai/ppt_generator.py @@ -1,16 +1,17 @@ import json +from typing import Union import openai from .domain.constants import MAX_CONTENT_LENGTH from .domain.exceptions import InvalidModel from .domain.prompts import ( DECK_CREATION_SYSTEM_PROMPT, - CHART_DATA_IDENTIFICATION, - BEST_CHART_FOR_DATA_SYSTEM_PROMPT, TITLE_GEN_SYSTEM_PROMPT, - FILENAME_SYSTEM_PROMPT + FILENAME_SYSTEM_PROMPT, + TOOL_USE_PROMPT, + SLIDE_CREATION_PROMPT ) from .ppt.ppt_creator import PowerPointCreator -from .utils.utils import format_simple_message_for_gpt, call_gpt_with_backoff +from .utils.utils import format_simple_message_for_gpt, call_gpt_with_backoff, generate_mermaid_diagram, parse_function_call_output class PowerPointGenerator: def __init__(self, openai_key: str, model: str = "gpt-4"): @@ -21,23 +22,40 @@ def __init__(self, openai_key: str, model: str = "gpt-4"): "name is correct or you have access to the model requested" ) self.model = model - - def create_powerpoint(self, user_input: str) -> str: + # str or list user_input + def create_powerpoint(self, user_input: Union[str, list]) -> str: """Generates a powerpoint based on the user's input""" - # identify if the user passed in data for a chart data_messages = format_simple_message_for_gpt( - system_message=CHART_DATA_IDENTIFICATION, user_message=user_input) + system_message=TOOL_USE_PROMPT, + user_message=f"This is the user input: \n{user_input}\n Analyze this and output in the given format.") data_response = call_gpt_with_backoff( messages=data_messages, temperature=0, max_length=MAX_CONTENT_LENGTH) + + + calls = parse_function_call_output(data_response) + + diagrams = [] + for func, param in calls: + if func != "none": + if func == "generate_chart": + best_chart_response = param + user_input += f"\nUse chart type: {best_chart_response}" + elif func == "generate_mermaid_diagram": + mermaid_text, name = param.split("@,@") + resp = generate_mermaid_diagram(mermaid_text=mermaid_text, filename=name+'.png') + if resp is None: + continue + diagrams.append(name) + + if diagrams != []: + diagrams = "\n".join([diagram+'.png' for diagram in diagrams]) + + user_input += f""" + We have some diagrams named: + + {diagrams} - # data was found in the input, determine which chart type fits the data best - if data_response.lower() == "data found": - best_chart_messages = format_simple_message_for_gpt( - system_message=BEST_CHART_FOR_DATA_SYSTEM_PROMPT, user_message=user_input) - best_chart_response = call_gpt_with_backoff( - messages=best_chart_messages, temperature=0) - # append this chart type instruction to the user input for deck creation - user_input += f"\nUse chart type: {best_chart_response}" + You can use them in your powerpoint.""" # create the deck based on the user input and load its json deck_messages = format_simple_message_for_gpt( @@ -63,3 +81,73 @@ def create_powerpoint(self, user_input: str) -> str: ppt = PowerPointCreator(title=title_response, slides_content=deck_json) ppt.create(file_name=filename_response) return filename_response + + + def create_powerpoint_from_outline(self, outline: list) -> str: + + deck = [] + for slide in outline: + user_input = slide + data_messages = format_simple_message_for_gpt( + system_message=TOOL_USE_PROMPT + "\n\n Not that there is no need to generate a diagram unless user asks you to generate one.", + user_message=f"This is the user input: \n{user_input}\n Analyze this and output in the given format.") + data_response = call_gpt_with_backoff( + messages=data_messages, temperature=0, max_length=MAX_CONTENT_LENGTH) + + calls = parse_function_call_output(data_response) + + # TODO(sirri69): THIS CAN BE ABSTRACTED IN A `update_prompt` function + diagrams = [] + for func, param in calls: + if func != "none": + if func == "generate_chart": + best_chart_response = param + user_input += f"\nUse chart type: {best_chart_response}" + elif func == "generate_mermaid_diagram": + mermaid_text, name = param.split("@,@") + resp = generate_mermaid_diagram(mermaid_text=mermaid_text, filename=name+'.png') + if resp is None: + continue + diagrams.append(name) + + if diagrams != []: + diagrams = "\n".join([diagram+'.png' for diagram in diagrams]) + + user_input += f""" + We have some diagrams named: + + {diagrams} + + You can use them in your powerpoint.""" + + # create the slide and append it to the deck + slide_messages = format_simple_message_for_gpt( + system_message=SLIDE_CREATION_PROMPT, user_message=user_input) + slide_response = call_gpt_with_backoff( + messages=slide_messages, temperature=0.2, max_length=MAX_CONTENT_LENGTH) + + slide_json = json.loads(slide_response) + deck.append(slide_json) + + + # TODO(sirri69) : Both title and filename can be generated in one call + title_messages = format_simple_message_for_gpt( + system_message=TITLE_GEN_SYSTEM_PROMPT, user_message=str(outline)) + title_response = call_gpt_with_backoff(messages=title_messages) + title = title_response.replace('"', '') + + filename_message = format_simple_message_for_gpt( + system_message=FILENAME_SYSTEM_PROMPT, user_message=title_response) + filename_response = call_gpt_with_backoff( + messages=filename_message, temperature=0) + filename_response = filename_response.replace('"', '') + + ppt = PowerPointCreator(title=title, slides_content=deck) + ppt.create(file_name=filename_response) + + return filename_response + + + + + diff --git a/powerpoint_generative_ai/utils/utils.py b/powerpoint_generative_ai/utils/utils.py index 98117d3..bc98f09 100644 --- a/powerpoint_generative_ai/utils/utils.py +++ b/powerpoint_generative_ai/utils/utils.py @@ -1,7 +1,11 @@ +import re import backoff import logging import openai from typing import List +import requests +import base64, zlib +from PIL import Image def setup_logger(name) -> logging.Logger: """ @@ -70,4 +74,47 @@ def call_gpt(messages: List, model: str = "gpt-4", temperature: float = 0.7, max frequency_penalty=0.0, top_p=1 ) - return response['choices'][0]['message']['content'] \ No newline at end of file + return response['choices'][0]['message']['content'] + + +def parse_function_call_output(input_text: str) -> list[str]: + """ + Removes the tags from the input_text, clean it and return the function name and params. + """ + + pattern = ".*?" + cleaned_text = re.sub(pattern, '', input_text, flags=re.DOTALL) + text = cleaned_text.replace("", "").replace("", "").strip() + + functions_calls = text.split("\n") + parsed_functions_calls = [] + for function_call in functions_calls: + function_name = function_call.split("<|?|>")[1] + param = function_call.split("<|?|>")[2] + + parsed_functions_calls.append([function_name, param]) + + return parsed_functions_calls + + +def generate_mermaid_diagram(mermaid_text: str, filename: str = "diagram.png"): + """ + Takes in mermaid syntax text and generates a diagram + """ + + encoded_mermaid_text = base64.urlsafe_b64encode(zlib.compress(mermaid_text.encode("utf-8"), 9)).decode("ascii") + + try: + url = f"https://kroki.io/mermaid/png/{encoded_mermaid_text}" + response = requests.get(url) + + + with open(filename, "wb") as f: + f.write(response.content) + + img = Image.open(filename) + except Exception as e: + print(e) + return None + + return filename diff --git a/test.py b/test.py new file mode 100644 index 0000000..b8267f3 --- /dev/null +++ b/test.py @@ -0,0 +1,415 @@ +from dotenv import load_dotenv +import os +load_dotenv() + + +from powerpoint_generative_ai.ppt_generator import PowerPointGenerator + +OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") + +USER_TEXTS = [ +"""create a six slide powerpoint about the growing obesity rate and its effect on health insurance premiums. + +here is some data for a chart: +x axis: 2010, 2012, 2014, 2016, 2018, 2020, 2022, 2024 +US: 5%, 10%, 15%, 20%, 25%, 30%, 35%, 40% +UK: 3%, 6%, 9%, 12%, 15%, 18%, 21%, 24% +RU: 2%, 4%, 6%, 8%, 10%, 12%, 14%, 16% +FR: 7%, 14%, 21%, 28%, 35%, 42%, 49%, 56% +IT: 1%, 2%, 3%, 4%, 5%, 6%, 7%, 8% + + +Also add a diagram about biology of fat cells. And a diagram about how sugar works. +""", +] + + +OUTLINE = [ + "Give a title to the presentation: Obesity and Health Insurance Premiums. Talk about how obesity effects health insurance premiums.", + """Talk about how obesity is a growing problem in the US, UK, RU, FR, IT. + here is some data for a chart: + x axis: 2010, 2012, 2014, 2016, 2018, 2020, 2022, 2024 + US: 5%, 10%, 15%, 20%, 25%, 30%, 35%, 40% + UK: 3%, 6%, 9%, 12%, 15%, 18%, 21%, 24% + RU: 2%, 4%, 6%, 8%, 10%, 12%, 14%, 16% + FR: 7%, 14%, 21%, 28%, 35%, 42%, 49%, 56% + IT: 1%, 2%, 3%, 4%, 5%, 6%, 7%, 8% + """, + "Educate the user on the biology of fat cells. Add a diagram about biology of fat cells.", + "Educate the user on how sugar works. Add a diagram about how sugar works.", + "Talk about the problems caused by obesity.", + "Provide an outro on the topic: Obesity and Health Insurance Premiums." +] + +OUTLINE2 = [ + """ + Bar graph with 3 bars as depicted below + Only need % and UCL plotted. + Include dotted like at 34.4% “Performance Goal” + UCL % should be included at top of UCL + + Total DRUG +N = 157 + +Proportion of patients with Event +15.9% +(UCL 21.3%) +(LCL 11%) + +======== + +DRUG Subgroup A +N = 41 + +2.4% +(UCL 10.2%) +(LCL 0%) + + +======== + +DRUG Subgroup B +N = 116 + +20.7% +(UCL 27.5%) +(LCL 0%) + + """, + + """ +Title: Incidence of AE TERM Following Treatment with DRUG Sub-title: National Health Reporting System in United States Between January 2021 – January 2022 [please make this look nice – not as important as main title – should be contained in title box though so searchable on iPad] + +Create Horizontal bar graph showing number of events for top 3 age groups (exclude 30+) + +Need to compare males vs females for each age category + +X-axis = Incidence of AE TERM (per 1 Million Doses) + +Please include placeholder footnote as client needs to include + +Since comparisons are between sex within age groups – try to separate age groups with vertical backfill for some other nice element so separated visually + +Data: +Age group,Males,Females +5 to 11,145,63 +12 to 17,456,263 +18 to 29,554,123 +30+,572,326 +""", + +""" +Title: enrollment distribution by aneurysm size and rupture status (ITT) + +Create two side-by-side bar graphs. (1-Left) unruptured (2-right) ruptured. + +Plot %s on Y-Axis by Sac Width on X-axis. Note – you need to calculate %s which is =(x/N)*100 + +Y-axis scales should be the same on both plots and should be in-line (eg 10% should be same on both plots so message not skewed when comparing data on two figures) + +Unruptured and ruptured bars should be different colors – not necessary to distinguish sac widths with different colors + +Include N in legend + +X-axis label = Aneurysm Sac Width (mm) + +Data: + +Extracted data from the table in the image in CSV format: + +Sac width,N,x,n +>3-4,5,141,1 +>4-5,29,141,9 +>5-6,27,141,3 +>6-7,32,141,1 +>7-8,28,141,1 +>8-9,13,141,- +>9-10,6,-,141 +>10-11,-,>11-12,1 +>11-12,1,141,- +""", + +""" +Title: study 910: primary endpoint demonstrates meaningful activity in patients with severe disease + +Create two stacked bar graphs showing ORR and CBR side by side and include the breakdown of responses within each – outlined below + +ORR = Overall Response Rate [spell out below figure] + +PR: partial response +VGPR: very good partial response +sCR: stringent complete response + +Note: you will plot percentages + +No need to label each piece of the stacked rather use color coded legend to distinguish response and number of patients. + +Above this stack – put a text box with overall ORR % and (95% CI) + + +==== + +CBR = Clinical Benefit Rate [spell out below figure] + +MR: minimal response +PR: partial response +VGPR: very good partial response +sCR: stringent complete response + +Note: you will plot percentages + +No need to label each piece of the stacked rather use color coded legend to distinguish response and number of patients. + +Above this stack – put a text box with overall CBR % and (95% CI) + + +====== + +CSV Data: + +Category,ORR a,n (%) +KCP-330-012 mITT (N = 122),31 (25.4),18.0, 34.1 +CBR b,n (%) +48 (39.3),30.6, 48.6 +Best Response +sCR/CR,n (%) +2 (1.6),0.2, 5.8 +GPR,n (%) +6 (4.9),1.8, 10.4 +PR,n (%) +23 (18.9),12.3, 26.9 +MR,n (%) +17 (13.9),8.3, 21.4 +SD,n (%) +48 (39.3),30.6, 48.6 +PD/NE,n (%) +26 (21.3),14.4, 29.6 + +""" +] + + +OUTLINE3 = [ + """ +Title: baseline demographics were balanced between groups + +Format table showing: +Age (years), mean (SD) +Female +While +Black or African American +Asian +Hispanic or Latino + +All will be %’s except for age + +Only show Drug / Placebo - Drug on left + +CSV Data, generate a table: +Category,ORR a,n (%) +KCP-330-012 mITT (N = 122),31 (25.4),18.0, 34.1 +CBR b,n (%) +48 (39.3),30.6, 48.6 +Best Response +sCR/CR,n (%) +2 (1.6),0.2, 5.8 +GPR,n (%) +6 (4.9),1.8, 10.4 +PR,n (%) +23 (18.9),12.3, 26.9 +MR,n (%) +17 (13.9),8.3, 21.4 +SD,n (%) +48 (39.3),30.6, 48.6 +PD/NE,n (%) +26 (21.3),14.4, 29.6 + +========= + +Generate a table using the data above. Do not generate a graph. +""", + """ + Title: resistance to multiple oral antibiotics further complicates treatment of cUTI in out-patient setting +Table should show: +Cefuroxime (β-lactam) +Ciprofloxacin (fluroquinolone) +Levofloxacin (fluroquinolone) +Trimethoprim-sulfamethoxazole + +Overarching table header: “Co-Resistant Agent (Class)” [REF - Critchley, 2019] +Show on Right [Column 3] +Show on Left [Column 2] +Bullet at bottom: 1 in 8 patients with cUTI infected with pathogen resistant to ≥ 3 most common classes of antibiotics + +CSV Data: +Agent,Trimethoprim-sulfamethoxazole (N=588),Levofloxacin (N=445) +Cefuroxime,31.3,45.7 +Ceftazidime,15.0,24.7 +Ciprofloxacin,44.2,100 +Levofloxacin,42.5,100 +Doripenem,0.0,0.0 +Ertapenem,0.3,0.5 +Imipenem,0.0,0.0 +Meropenem,0.0,0.0 +Trimethoprim-sulfamethoxazole,100,56.2 + +""", +""" +Bar graph with 3 bars as depicted below + Only need % and UCL plotted. + Include dotted like at 34.4% “Performance Goal” + UCL % should be included at top of UCL + + Total DRUG +N = 157 + +Proportion of patients with Event +15.9% +(UCL 21.3%) +(LCL 11%) + +======== + +DRUG Subgroup A +N = 41 + +2.4% +(UCL 10.2%) +(LCL 0%) + + +======== + +DRUG Subgroup B +N = 116 + +20.7% +(UCL 27.5%) +(LCL 0%) + + +""", +""" +Title: Incidence of AE TERM Following Treatment with DRUG Sub-title: National Health Reporting System in United States Between January 2021 – January 2022 [please make this look nice – not as important as main title – should be contained in title box though so searchable on iPad] + +Create Horizontal bar graph showing number of events for top 3 age groups (exclude 30+) + +Need to compare males vs females for each age category + +X-axis = Incidence of AE TERM (per 1 Million Doses) + +Please include placeholder footnote as client needs to include + +Since comparisons are between sex within age groups – try to separate age groups with vertical backfill for some other nice element so separated visually + +Data: +Age group,Males,Females +5 to 11,145,63 +12 to 17,456,263 +18 to 29,554,123 +30+,572,326 +""", +""" +Title: enrollment distribution by aneurysm size and rupture status (ITT) + +Create two side-by-side bar graphs. (1-Left) unruptured (2-right) ruptured. + +Plot %s on Y-Axis by Sac Width on X-axis. Note – you need to calculate %s which is =(x/N)*100 + +Y-axis scales should be the same on both plots and should be in-line (eg 10% should be same on both plots so message not skewed when comparing data on two figures) + +Unruptured and ruptured bars should be different colors – not necessary to distinguish sac widths with different colors + +Include N in legend + +X-axis label = Aneurysm Sac Width (mm) + +Data: + +Extracted data from the table in the image in CSV format: + +Sac width,N,x,n +>3-4,5,141,1 +>4-5,29,141,9 +>5-6,27,141,3 +>6-7,32,141,1 +>7-8,28,141,1 +>8-9,13,141,- +>9-10,6,-,141 +>10-11,-,>11-12,1 +>11-12,1,141,- +""", +""" +Title: study 910: primary endpoint demonstrates meaningful activity in patients with severe disease + +Create two stacked bar graphs showing ORR and CBR side by side and include the breakdown of responses within each – outlined below + +ORR = Overall Response Rate [spell out below figure] + +PR: partial response +VGPR: very good partial response +sCR: stringent complete response + +Note: you will plot percentages + +No need to label each piece of the stacked rather use color coded legend to distinguish response and number of patients. + +Above this stack – put a text box with overall ORR % and (95% CI) + + +==== + +CBR = Clinical Benefit Rate [spell out below figure] + +MR: minimal response +PR: partial response +VGPR: very good partial response +sCR: stringent complete response + +Note: you will plot percentages + +No need to label each piece of the stacked rather use color coded legend to distinguish response and number of patients. + +Above this stack – put a text box with overall CBR % and (95% CI) + + +====== + +CSV Data: + +Category,ORR a,n (%) +KCP-330-012 mITT (N = 122),31 (25.4),18.0, 34.1 +CBR b,n (%) +48 (39.3),30.6, 48.6 +Best Response +sCR/CR,n (%) +2 (1.6),0.2, 5.8 +GPR,n (%) +6 (4.9),1.8, 10.4 +PR,n (%) +23 (18.9),12.3, 26.9 +MR,n (%) +17 (13.9),8.3, 21.4 +SD,n (%) +48 (39.3),30.6, 48.6 +PD/NE,n (%) +26 (21.3),14.4, 29.6 + +""" + + +] + + +def generate_ppt(): + ppt_generator = PowerPointGenerator(OPENAI_API_KEY) + powerpoint_files = [ppt_generator.create_powerpoint(user_input=user_text) for user_text in USER_TEXTS] + +def generate_ppt_from_outline(): + ppt_generator = PowerPointGenerator(OPENAI_API_KEY) + powerpoint_files = ppt_generator.create_powerpoint_from_outline(outline=[OUTLINE3[0]]) + print(powerpoint_files) + +if __name__ == "__main__": + # generate_ppt() + generate_ppt_from_outline() \ No newline at end of file