para-lost commited on
Commit
2dd1349
·
1 Parent(s): 0cde187

add slides lib

Browse files
SlidesLib/README.md ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SlidesLib
2
+
3
+ SlidesLib is a Python library for slide generation, providing APIs for image generation, Google search, and slide customization.
4
+
5
+ ## Features
6
+ - **Image Generation**: Create images using the DALL-E API.
7
+ - **Search Integration**: Perform Google searches, save screenshots, and retrieve images.
8
+ - **Slide Customization**: Add text, bullet points, images, and set slide backgrounds.
9
+
10
+ ## Installation
11
+ 1. **Dependencies**: Install required Python libraries:
12
+ ```bash
13
+ pip install -r requirements.txt
14
+ ```
15
+ 2. **Google Chrome**: Required for search functionality:
16
+ ```bash
17
+ wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
18
+ sudo dpkg -i google-chrome-stable_current_amd64.deb
19
+ sudo apt-get install -f
20
+ ```
21
+ 3. **OpenAI API Key**: Export your API key:
22
+ ```bash
23
+ export OPENAI_API_KEY="your_api_key"
24
+ ```
25
+
26
+ ## Quick Start
27
+ - **Image Generation**:
28
+ ```python
29
+ from slidesLib.image_gen import Dalle3
30
+ Dalle3.generate_image("A futuristic cityscape", save_path="cityscape.png")
31
+ ```
32
+
33
+ - **Search Integration**:
34
+ ```python
35
+ from slidesLib.search import GoogleSearch
36
+ GoogleSearch.search_result("Tallest building in the world", "result.png")
37
+ ```
38
+
39
+ - **Slide Customization**:
40
+ ```python
41
+ from slidesLib.ppt_gen import add_title
42
+ add_title(slide, text="Welcome to SlidesLib")
43
+ ```
44
+
45
+ For more examples, refer to the code in this folder.
46
+ ```
SlidesLib/__init__.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .search import GoogleSearch
2
+ from .vqa import VQA
3
+ from .image_gen import Dalle3
4
+ from .llm import LLM
5
+ from .ppt_gen import SlideAgent
6
+ from pptx.util import Inches, Pt
7
+ from pptx.dml.color import RGBColor
8
+ from pptx.enum.text import MSO_AUTO_SIZE
9
+ from mysearchlib import LLM
10
+ from pptx.dml.color import RGBColor
11
+ def search_result(question: str, screenshot_path: str = "screenshot.png") -> str:
12
+ """
13
+ Search a question on Google, and take a screenshot of the search result.
14
+ Save the screenshot to screenshot_path, and return the path.
15
+ """
16
+ return GoogleSearch.search_result(question, screenshot_path)
17
+
18
+ def search_image(query: str, save_path: str = 'top_images') -> str:
19
+ """
20
+ Search for an image on Google and download the result to download_path.
21
+ Return download_path.
22
+ """
23
+ return GoogleSearch.search_image(query, save_path)
24
+
25
+ def get_answer(question: str) -> str:
26
+ """
27
+ Calls the LLM by inputing a question,
28
+ then get the response of the LLM as the answer
29
+ """
30
+ return LLM.get_answer(question)
31
+
32
+ def get_code(request:str, examples:str = "") -> str:
33
+ """
34
+ Calls the LLM to generate code for a request.
35
+ request: the task that the model should conduct
36
+ examples: few-shot code examples for the request
37
+ """
38
+ return LLM.get_answer(request, examples)
39
+
40
+ def generate_image(query: str, save_path: str = "downloaded_image.png") -> str:
41
+ """
42
+ Generate an image based on a text query, save the image to the save_path
43
+ Return the path of the saved image.
44
+ """
45
+ return Dalle3.generate_image(query, save_path)
46
+
47
+
48
+ def add_title(
49
+ slide, text: str, font_size: int = 44,
50
+ font_color: tuple[int, int, int] = (0, 0, 0),
51
+ background_color: tuple[int, int, int] = None,
52
+ ):
53
+ """Add a title text to the slide with custom font size and font color (RGB tuple).
54
+ Args:
55
+ slide: Slide object as in pptx library
56
+ text: str, Title text to be added
57
+ font_size: int, Font size in int (point size), e.g., 44
58
+ font_color: tuple(int,int,int), RGB color, e.g., (0, 0, 0)
59
+ background_color: Optional, tuple(int,int,int), RGB color, e.g., (255, 255, 255)
60
+ Rets:
61
+ slide: Slide object with the title added
62
+ """
63
+ title_shape = slide.shapes.title
64
+ if title_shape is None:
65
+ # Add a new text box as the title if no placeholder is found
66
+ title_shape = slide.shapes.add_textbox(Inches(1), Inches(0.5), Inches(8), Inches(1))
67
+ title_shape.text = text
68
+ for paragraph in title_shape.text_frame.paragraphs:
69
+ paragraph.font.size = Pt(font_size)
70
+ paragraph.font.color.rgb = RGBColor(*font_color)
71
+ if background_color is not None:
72
+ title_shape.fill.solid()
73
+ title_shape.fill.fore_color.rgb = RGBColor(*background_color)
74
+ return slide
75
+
76
+
77
+ def add_text(
78
+ slide, text: str, coords: list[float],
79
+ font_size: int = 20, bold: bool = False,
80
+ color: tuple[int, int, int] = (0, 0, 0),
81
+ background_color: tuple[int, int, int] = None,
82
+ auto_size: bool = True,
83
+ ):
84
+ """Add a text box at a specified location with custom text and color settings.
85
+ Args:
86
+ slide: Slide object as in pptx library
87
+ text: str, Text to be added
88
+ coords: list(float), [left, top, width, height] in inches
89
+ font_size: int, Font size in int (point size), e.g., 20
90
+ bold: bool, True if bold-type the text, False otherwise
91
+ color: tuple(int,int,int), RGB color, e.g., (0, 0, 0)
92
+ background_color: Optional, tuple(int,int,int), RGB color, e.g., (255, 255, 255)
93
+ auto_size: bool, True if auto-size the text box, False otherwise
94
+ Rets:
95
+ slide: Slide object with the text box added
96
+ """
97
+ # Create the text box shape
98
+ left, top, width, height = coords
99
+ text_box = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(height))
100
+
101
+ # Set background color if provided
102
+ if background_color:
103
+ text_box.fill.solid()
104
+ text_box.fill.fore_color.rgb = RGBColor(*background_color)
105
+ else:
106
+ text_box.fill.background() # No fill if no color is specified
107
+
108
+ # Handle line breaks and adjust height
109
+ lines = text.split("\n")
110
+ adjusted_height = height * len(lines) # Adjust height based on the number of lines
111
+ text_box.height = Inches(adjusted_height)
112
+
113
+ # Set text and format it
114
+ text_frame = text_box.text_frame
115
+ text_frame.word_wrap = True
116
+ if auto_size:
117
+ text_frame.auto_size = MSO_AUTO_SIZE.SHAPE_TO_FIT_TEXT # Automatically fit the text box to the text
118
+
119
+ p = text_frame.add_paragraph()
120
+ p.text = text
121
+ p.font.size = Pt(font_size)
122
+ p.font.bold = bold
123
+ p.font.color.rgb = RGBColor(*color)
124
+ return slide
125
+
126
+
127
+ def add_bullet_points(
128
+ slide, bullet_points: list[str], coords: list[float],
129
+ font_size: int = 18, color: tuple[int, int, int] = (0, 0, 0),
130
+ background_color: tuple[int, int, int] = None,
131
+ ):
132
+ """Add a text box with bullet points.
133
+ Args:
134
+ slide: Slide object as in pptx library
135
+ bullet_points: list(str), List of texts to be added as bullet points
136
+ coords: list(float), [left, top, width, height] in inches
137
+ font_size: int, Font size in int (point size), e.g., 18
138
+ color: tuple(int,int,int), RGB color, e.g., (0, 0, 0)
139
+ background_color: Optional, tuple(int,int,int), RGB color, e.g., (255, 255, 255)
140
+ Rets:
141
+ slide: Slide object with the bullet points added
142
+ """
143
+ left, top, width, height = coords
144
+ text_box = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(height))
145
+ # Set background color if provided
146
+ if background_color:
147
+ text_box.fill.solid()
148
+ text_box.fill.fore_color.rgb = RGBColor(*background_color)
149
+ else:
150
+ text_box.fill.background() # No fill if no color is specified
151
+
152
+ text_frame = text_box.text_frame
153
+ text_frame.word_wrap = True
154
+ text_frame.auto_size = MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE
155
+
156
+ for point in bullet_points:
157
+ p = text_frame.add_paragraph()
158
+ p.text = point
159
+ p.font.size = Pt(font_size)
160
+ p.font.color.rgb = RGBColor(*color)
161
+ # p.level = bullet_points.index(point)
162
+
163
+ return slide
164
+
165
+
166
+ def add_image(slide, image_path: str, coords: list[float]):
167
+ """Add an image in the provided path to the specified coords and sizes.
168
+ Args:
169
+ slide: Slide object as in pptx library
170
+ image_path: str, Path to the image file
171
+ coords: list(float), [left, top, width, height] in inches
172
+ Rets:
173
+ slide: Slide object with the image added
174
+ """
175
+ left, top, width, height = coords
176
+ slide.shapes.add_picture(image_path, Inches(left), Inches(top), Inches(width), Inches(height))
177
+ return slide
178
+
179
+
180
+ def set_background_color(slide, color: tuple[int, int, int]):
181
+ """Set background color for the current slide.
182
+ Args:
183
+ slide: Slide object as in pptx library
184
+ color: tuple(int, int, int), RGB color, e.g., (255, 255, 255)
185
+ Returns:
186
+ modified slide object
187
+ """
188
+ fill = slide.background.fill
189
+ fill.solid()
190
+ fill.fore_color.rgb = RGBColor(*color) # Convert tuple to RGBColor
191
+ return slide
SlidesLib/image_gen.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from openai import OpenAI
2
+ import requests
3
+
4
+ class Dalle3():
5
+ @classmethod
6
+ def __init_dalle__(cls):
7
+ client = OpenAI()
8
+ return client
9
+
10
+ @classmethod
11
+ def generate_image(cls, query: str, save_path: str = "downloaded_image.png"):
12
+ """Generate an image based on a text query, save the image to the save_path"""
13
+ client = cls.__init_dalle__()
14
+ response = client.images.generate(
15
+ model="dall-e-3",
16
+ prompt=query,
17
+ size="1024x1024",
18
+ quality="standard",
19
+ n=1,
20
+ )
21
+ image_url = response.data[0].url
22
+ # Send a GET request to the URL
23
+ response = requests.get(image_url)
24
+
25
+ # Open a file in binary write mode and write the content of the response
26
+ with open(save_path, "wb") as file:
27
+ file.write(response.content)
28
+ return save_path
SlidesLib/llm.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from openai import OpenAI
2
+ import requests
3
+
4
+ class LLM():
5
+ """Calls the LLM"""
6
+ @classmethod
7
+ def __init_llm__(cls):
8
+ client = OpenAI()
9
+ code_prompt = "Directly Generate executable python code for the following request:\n"
10
+ return client, code_prompt
11
+ @classmethod
12
+ def get_answer(cls, question: str):
13
+ """Calls the LLM by inputing a question,
14
+ then get the response of the LLM as the answer"""
15
+ client, code_prompt = cls.__init_llm__()
16
+ response = client.chat.completions.create(
17
+ model="gpt-4o-mini",
18
+ messages=[
19
+ {"role": "system", "content": "You are a helpful assistant."},
20
+ {"role": "user", "content": question}
21
+ ]
22
+ )
23
+ return response.choices[0].message.content
24
+
25
+ @classmethod
26
+ def get_code(cls, request:str, examples:str = ""):
27
+ """
28
+ Calls the LLM to generate code for a request.
29
+ request: the task that the model should conduct
30
+ examples: few-shot code examples for the request
31
+ """
32
+ client, code_prompt = cls.__init_llm__()
33
+ code = cls.get_answer(code_prompt + examples + request)
34
+ return code
SlidesLib/plotting.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Call Matplotlib Library to draw graphs (Bar/Plot...)
2
+ import matplotlib.pyplot as plt
3
+ from llm import *
4
+ class Plotting:
5
+ def bar_plot(self, data: dict, title: str, xlabel: str, ylabel: str, output_path: str = 'bar_plot.png'):
6
+ """
7
+ Create a bar plot.
8
+
9
+ :param data: Dictionary containing data to plot (keys as labels, values as heights).
10
+ :param title: Title of the plot.
11
+ :param xlabel: Label for the X-axis.
12
+ :param ylabel: Label for the Y-axis.
13
+ :param output_path: Path to save the plot image.
14
+ """
15
+ labels = list(data.keys())
16
+ heights = list(data.values())
17
+
18
+ plt.figure(figsize=(10, 6))
19
+ plt.bar(labels, heights, color='skyblue')
20
+ plt.title(title)
21
+ plt.xlabel(xlabel)
22
+ plt.ylabel(ylabel)
23
+ plt.tight_layout()
24
+ plt.savefig(output_path)
25
+ plt.close()
26
+
27
+ return output_path
28
+
29
+ def line_plot(self, data: dict, title: str, xlabel: str, ylabel: str, output_path: str = 'line_plot.png'):
30
+ """
31
+ Create a line plot.
32
+
33
+ :param data: Dictionary containing data to plot (keys as x-values, values as y-values).
34
+ :param title: Title of the plot.
35
+ :param xlabel: Label for the X-axis.
36
+ :param ylabel: Label for the Y-axis.
37
+ :param output_path: Path to save the plot image.
38
+ """
39
+ x_values = list(data.keys())
40
+ y_values = list(data.values())
41
+
42
+ plt.figure(figsize=(10, 6))
43
+ plt.plot(x_values, y_values, marker='o', color='skyblue')
44
+ plt.title(title)
45
+ plt.xlabel(xlabel)
46
+ plt.ylabel(ylabel)
47
+ plt.grid(True)
48
+ plt.tight_layout()
49
+ plt.savefig(output_path)
50
+ plt.close()
51
+
52
+ return output_path
53
+
54
+ def get_plot(self, data):
55
+ instruction = ""
56
+
SlidesLib/ppt_gen.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pptx import Presentation
2
+ from pptx.util import Inches as _Inches, Pt as _Pt
3
+ from pptx.dml.color import RGBColor
4
+ from pptx.enum.text import PP_ALIGN, MSO_AUTO_SIZE
5
+ from pptx.enum.shapes import MSO_AUTO_SHAPE_TYPE, MSO_SHAPE_TYPE
6
+ from io import BytesIO
7
+
8
+ ARROW_ADD = '"""<a:tailEnd type="arrow" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"/>"""'
9
+
10
+ class SlideAgent:
11
+ def __init__(self, slide_width=13.33, slide_height=7.5):
12
+ """Initialize a new presentation with specified slide dimensions in inches."""
13
+ self.prs = Presentation()
14
+ self.prs.slide_width = self._inches(slide_width)
15
+ self.prs.slide_height = self._inches(slide_height)
16
+ self.slide = None
17
+
18
+ def _inches(self, val):
19
+ """Helper method to convert to Inches."""
20
+ return _Inches(val)
21
+
22
+ def _points(self, val):
23
+ """Helper method to convert to Points."""
24
+ return _Pt(val)
25
+
26
+ # ------- Slide APIs -------
27
+ def add_slide(self, layout=0):
28
+ """Create a new slide with a specific layout."""
29
+ slide_layout = self.prs.slide_layouts[layout]
30
+ self.slide = self.prs.slides.add_slide(slide_layout)
31
+
32
+ # ------- Text APIs -------
33
+ def add_title(self, text, font_size=44, font_color=(0, 0, 0)):
34
+ """Add a title to the slide with a custom font size (in points) and font color (RGB tuple)."""
35
+ title_shape = self.slide.shapes.title
36
+ title_shape.text = text
37
+ self._format_text(title_shape.text_frame, self._points(font_size), RGBColor(*font_color))
38
+
39
+ def add_text(self, text, top, left, width, height, font_size=20, bold=False, color=(0, 0, 0), background_color=None, auto_size=True):
40
+ """Add a text box at a specified location with custom text settings and optional background color."""
41
+ # Create the text box shape
42
+ text_box = self.slide.shapes.add_textbox(self._inches(left), self._inches(top), self._inches(width), self._inches(height))
43
+
44
+ # Set background color if provided
45
+ if background_color:
46
+ text_box.fill.solid()
47
+ text_box.fill.fore_color.rgb = RGBColor(*background_color)
48
+ else:
49
+ text_box.fill.background() # No fill if no color is specified
50
+
51
+ # Handle line breaks and adjust height
52
+ lines = text.split("\n")
53
+ adjusted_height = height * len(lines) # Adjust height based on the number of lines
54
+ text_box.height = self._inches(adjusted_height)
55
+
56
+ # Set text and format it
57
+ text_frame = text_box.text_frame
58
+ text_frame.word_wrap = True
59
+ if auto_size:
60
+ text_frame.auto_size = MSO_AUTO_SIZE.SHAPE_TO_FIT_TEXT # Automatically fit the text box to the text
61
+ self._format_paragraph(text_frame, text, self._points(font_size), bold, RGBColor(*color))
62
+
63
+ def add_bullet_points(self, bullet_points, top, left, width, height, font_size=18, color=(0, 0, 0)):
64
+ """Add a text box with bullet points."""
65
+ text_box = self.slide.shapes.add_textbox(self._inches(left), self._inches(top), self._inches(width), self._inches(height))
66
+ text_frame = text_box.text_frame
67
+ text_frame.word_wrap = True
68
+ text_frame.auto_size = MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE
69
+
70
+ for point in bullet_points:
71
+ p = text_frame.add_paragraph()
72
+ p.text = point
73
+ self._format_text(p, self._points(font_size), RGBColor(*color))
74
+ p.level = bullet_points.index(point)
75
+
76
+ # ------- Image APIs -------
77
+ def add_image(self, image_path, top, left, width, height):
78
+ """Add an image at a specified location."""
79
+ self.slide.shapes.add_picture(image_path, self._inches(left), self._inches(top), self._inches(width), self._inches(height))
80
+
81
+ def add_image_centered(self, image_path, image_width, image_height):
82
+ """Add an image centered on the slide."""
83
+ slide_width = self.prs.slide_width.inches
84
+ slide_height = self.prs.slide_height.inches
85
+ left = (slide_width - image_width) / 2
86
+ top = (slide_height - image_height) / 2
87
+ self.add_image(image_path, top, left, image_width, image_height)
88
+
89
+ # ------- Shape APIs -------
90
+ def add_shape(self, shape_type, top, left, width, height, fill_color=None):
91
+ """Add a shape to the slide, supporting MSO_AUTO_SHAPE_TYPE."""
92
+ if isinstance(shape_type, str):
93
+ # Check if the shape type is a valid string, otherwise raise an error
94
+ try:
95
+ shape_type = getattr(MSO_AUTO_SHAPE_TYPE, shape_type.upper())
96
+ except AttributeError:
97
+ raise ValueError(f"Invalid shape type: {shape_type}. Must be a valid MSO_AUTO_SHAPE_TYPE.")
98
+
99
+ # Now create the shape with the validated or passed enum type
100
+ shape = self.slide.shapes.add_shape(shape_type, self._inches(left), self._inches(top), self._inches(width), self._inches(height))
101
+
102
+ if fill_color:
103
+ shape.fill.solid()
104
+ shape.fill.fore_color.rgb = RGBColor(*fill_color)
105
+
106
+ def add_straight_arrow(self, start_x, start_y, end_x, end_y):
107
+ connector = self.slide.shapes.add_connector("MSO_CONNECTOR.STRAIGHT", start_x, start_y, end_x, end_y)
108
+
109
+
110
+ def add_straight_line(self, start_x, start_y, end_x, end_y):
111
+ connector = self.slide.shapes.add_connector("MSO_CONNECTOR.STRAIGHT", start_x, start_y, end_x, end_y)
112
+ line_elem = connector.line._get_or_add_ln()
113
+ line_elem.append(parse_xml({ARROW_ADD}))
114
+
115
+ # ------- Table APIs -------
116
+ def add_table(self, rows, cols, top, left, width, height, column_widths=None):
117
+ """Add a table to the slide."""
118
+ table = self.slide.shapes.add_table(rows, cols, left, top, width, height).table
119
+ if column_widths:
120
+ for idx, col_width in enumerate(column_widths):
121
+ table.columns[idx].width = Inches(col_width)
122
+ return table
123
+
124
+ # ------- Helper APIs -------
125
+ def set_background_color(self, color):
126
+ """Set background color for the current slide."""
127
+ background = self.slide.background
128
+ fill = background.fill
129
+ fill.solid()
130
+ fill.fore_color.rgb = color
131
+
132
+ def duplicate_slide(self, slide_index):
133
+ """Duplicate a slide by index."""
134
+ template_slide = self.prs.slides[slide_index]
135
+ new_slide = self.prs.slides.add_slide(template_slide.slide_layout)
136
+ for shape in template_slide.shapes:
137
+ self._copy_shape(shape, new_slide)
138
+
139
+ def save_presentation(self, file_name):
140
+ """Save the PowerPoint presentation."""
141
+ self.prs.save(file_name)
142
+
143
+ # ------- Internal Helper Methods -------
144
+ def _format_paragraph(self, text_frame, text, font_size, bold, color):
145
+ """Helper function to format text within a text frame."""
146
+ p = text_frame.add_paragraph()
147
+ p.text = text
148
+ p.font.size = font_size
149
+ p.font.bold = bold
150
+ p.font.color.rgb = color
151
+
152
+ def _format_text(self, text_frame, font_size, font_color):
153
+ """Helper function to format text in a text frame."""
154
+ for paragraph in text_frame.paragraphs:
155
+ paragraph.font.size = font_size
156
+ paragraph.font.color.rgb = font_color
157
+
158
+ def _copy_shape(self, shape, slide):
159
+ """Copy a shape from one slide to another."""
160
+ if shape.shape_type == MSO_SHAPE_TYPE.PICTURE:
161
+ image = BytesIO(shape.image.blob)
162
+ slide.shapes.add_picture(image, shape.left, shape.top, shape.width, shape.height)
163
+ elif shape.has_text_frame:
164
+ new_shape = slide.shapes.add_textbox(shape.left, shape.top, shape.width, shape.height)
165
+ new_shape.text = shape.text
166
+ self._format_text(new_shape.text_frame, shape.text_frame.paragraphs[0].font.size, shape.text_frame.paragraphs[0].font.color.rgb)
167
+
168
+
169
+
170
+
171
+
172
+
173
+
SlidesLib/search.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from selenium import webdriver
2
+ from selenium.webdriver.chrome.service import Service
3
+ from selenium.webdriver.chrome.options import Options
4
+ from chromedriver_py import binary_path
5
+ from selenium.webdriver.common.by import By
6
+ from selenium.webdriver.support.ui import WebDriverWait
7
+ from selenium.webdriver.support import expected_conditions as EC
8
+ import time
9
+ import requests
10
+ from selenium.webdriver.common.keys import Keys
11
+ from google_images_download import google_images_download
12
+ from bing_image_downloader import downloader
13
+ import os
14
+ import shutil
15
+ class GoogleSearch:
16
+ @classmethod
17
+ def _init_driver(cls):
18
+ chrome_options = Options()
19
+ chrome_options.add_argument("--headless")
20
+ chrome_options.add_argument("--disable-gpu")
21
+ chrome_options.add_argument("--no-sandbox")
22
+ chrome_options.add_argument("--disable-dev-shm-usage")
23
+ service = Service(binary_path)
24
+ driver = webdriver.Chrome(service=service, options=chrome_options)
25
+ wait = WebDriverWait(driver, 100)
26
+ return driver, wait
27
+
28
+ @classmethod
29
+ def search_result(cls, question: str, screenshot_path: str = "screenshot.png") -> str:
30
+ """Search a question on Google and return a screenshot of the search result."""
31
+ driver, wait = cls._init_driver()
32
+
33
+ if not question:
34
+ raise ValueError("Please provide a question")
35
+
36
+ # Perform Google search
37
+ search_url = f"https://www.google.com/search?q={question}"
38
+ driver.get(search_url)
39
+
40
+ # Give some time for the page to load
41
+ time.sleep(3)
42
+
43
+ # Take a screenshot
44
+ driver.save_screenshot(screenshot_path)
45
+
46
+ driver.quit()
47
+ return screenshot_path
48
+
49
+ @classmethod
50
+ def search_image_org(cls, query: str, download_path: str = 'top_image.png') -> str:
51
+ """Search for an image on Google and download the top result."""
52
+ driver, wait = cls._init_driver()
53
+
54
+ if not query:
55
+ raise ValueError("Please provide a query")
56
+
57
+ # Perform Google image search
58
+ search_url = f"https://www.google.com/search?tbm=isch&q={query}"
59
+ driver.get(search_url)
60
+
61
+ # Find all image elements
62
+ image_elements = driver.find_elements(By.CSS_SELECTOR, "img")
63
+
64
+ # Filter out Google icon images and get the first valid image URL
65
+ image_url = None
66
+ for img in image_elements:
67
+ src = img.get_attribute("src")
68
+ if src and "googlelogo" not in src:
69
+ image_url = src
70
+ try:
71
+ response = requests.get(image_url)
72
+ with open(download_path, 'wb') as file:
73
+ file.write(response.content)
74
+
75
+ driver.quit()
76
+ print(image_url)
77
+ return download_path
78
+ except Exception:
79
+ print("Error downloading image, skipping.")
80
+ continue
81
+
82
+ driver.quit()
83
+ raise Exception("No valid image found")
84
+
85
+ @classmethod
86
+ def search_image_prev(cls, query, output_dir='./downloads', limit=10):
87
+ # Download images using Bing Image Downloader
88
+ downloader.download(query, limit=limit, output_dir=output_dir, adult_filter_off=True, force_replace=False, timeout=60)
89
+ # List the files in the output directory
90
+ image_dir = os.path.join(output_dir, query)
91
+ if not os.path.exists(image_dir):
92
+ raise FileNotFoundError(f"No images found for query '{query}' in directory '{output_dir}'")
93
+
94
+ # Collect all image paths
95
+ image_paths = [os.path.join(image_dir, file) for file in os.listdir(image_dir) if file.endswith(('jpg', 'jpeg', 'png'))]
96
+
97
+ # Return the first image
98
+ return image_paths[0]
99
+
100
+ @classmethod
101
+ def search_image_prev(cls, query, output_dir='./downloads', limit=10):
102
+ # Download images using Bing Image Downloader
103
+ downloader.download(query, limit=limit, output_dir=output_dir, adult_filter_off=True, force_replace=False, timeout=60)
104
+ # List the files in the output directory
105
+ image_dir = os.path.join(output_dir, query)
106
+ if not os.path.exists(image_dir):
107
+ raise FileNotFoundError(f"No images found for query '{query}' in directory '{output_dir}'")
108
+
109
+ # Collect all image paths
110
+ image_paths = [os.path.join(image_dir, file) for file in os.listdir(image_dir) if file.endswith(('jpg', 'jpeg', 'png'))]
111
+
112
+ # Return the first image
113
+ return image_paths[0]
114
+
115
+ @classmethod
116
+ def search_image(cls, query, save_path):
117
+ """
118
+ Search for an image based on the query and save the result to the specified path.
119
+
120
+ Args:
121
+ query (str): The query to search for.
122
+ save_path (str): The path to save the downloaded image.
123
+
124
+ Returns:
125
+ str: The path where the image was saved.
126
+ """
127
+ # Create a temporary directory for storing downloaded images
128
+ temp_dir = "./temp_download"
129
+ os.makedirs(temp_dir, exist_ok=True)
130
+
131
+ # Download only the top image result
132
+ downloader.download(query, limit=1, output_dir=temp_dir, adult_filter_off=True, force_replace=True, timeout=60)
133
+
134
+ # Construct the expected directory and image path
135
+ image_dir = os.path.join(temp_dir, query)
136
+ image_files = [file for file in os.listdir(image_dir) if file.endswith(('jpg', 'jpeg', 'png'))]
137
+
138
+ # Check if any image files were downloaded
139
+ if not image_files:
140
+ raise FileNotFoundError(f"No images found for query '{query}'.")
141
+
142
+ # Copy the top image to the desired save path
143
+ top_image_path = os.path.join(image_dir, image_files[0])
144
+ shutil.move(top_image_path, save_path)
145
+
146
+ # Clean up temporary directory
147
+ shutil.rmtree(temp_dir)
148
+
149
+ return save_path
SlidesLib/vqa.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import BlipProcessor, BlipForQuestionAnswering
2
+ from PIL import Image
3
+ import requests
4
+ import re
5
+ class VQA:
6
+ def __init__(self, gpu_number=0):
7
+ use_load_8bit= False
8
+ from transformers import AutoProcessor, InstructBlipForConditionalGeneration, InstructBlipProcessor
9
+
10
+
11
+ self.model = InstructBlipForConditionalGeneration.from_pretrained("Salesforce/instructblip-vicuna-7b", device_map="auto")
12
+ self.processor = InstructBlipProcessor.from_pretrained("Salesforce/instructblip-vicuna-7b")
13
+
14
+ self.model.eval()
15
+ self.qa_prompt = "Question: {} Short answer:"
16
+ self.caption_prompt = "\n<image>\na photo of"
17
+ self.max_words = 50
18
+
19
+ def pre_question(self, question):
20
+ # from LAVIS blip_processors
21
+ question = re.sub(
22
+ r"([.!\"()*#:;~])",
23
+ "",
24
+ question.lower(),
25
+ )
26
+ question = question.rstrip(" ")
27
+
28
+ # truncate question
29
+ question_words = question.split(" ")
30
+ if len(question_words) > self.max_words:
31
+ question = " ".join(question_words[: self.max_words])
32
+
33
+ return question
34
+
35
+ def qa(self, image_path, question):
36
+ image = Image.open(image_path)
37
+ question = self.pre_question(question)
38
+ inputs = self.processor(images=image, text=question, return_tensors="pt", padding="longest").to(self.model.device)
39
+ generated_ids = self.model.generate(**inputs, length_penalty=-1, num_beams=5, max_length=30, min_length=1,
40
+ do_sample=False, top_p=0.9, repetition_penalty=1.0,
41
+ num_return_sequences=1, temperature=1)
42
+ generated_text = self.processor.batch_decode(generated_ids, skip_special_tokens=True)
43
+
44
+ return generated_text[0]
requirements.txt CHANGED
@@ -4,5 +4,4 @@ openai
4
  python-pptx
5
  numpy
6
  colormath
7
- scipy
8
- -e ./SlidesAgent
 
4
  python-pptx
5
  numpy
6
  colormath
7
+ scipy