Spaces:
Restarting
Restarting
Duplicate from Alpaca233/ChatGPT-PPT-Generate
Browse filesCo-authored-by: Alpaca <Alpaca233@users.noreply.huggingface.co>
- .gitattributes +34 -0
- README.md +15 -0
- app.py +245 -0
- requirements.txt +4 -0
- theme.pptx +0 -0
.gitattributes
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
29 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
30 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
31 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
32 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
33 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
34 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: ChatGPT PPT Generate
|
3 |
+
emoji: 🌍
|
4 |
+
colorFrom: pink
|
5 |
+
colorTo: indigo
|
6 |
+
sdk: gradio
|
7 |
+
sdk_version: 3.21.0
|
8 |
+
app_file: app.py
|
9 |
+
pinned: false
|
10 |
+
duplicated_from: Alpaca233/ChatGPT-PPT-Generate
|
11 |
+
---
|
12 |
+
|
13 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
14 |
+
|
15 |
+
form [here](https://github.com/AmNotAGoose/Python-PPTX-ChatGPT-Presentation-Generator)
|
app.py
ADDED
@@ -0,0 +1,245 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import glob
|
2 |
+
import os
|
3 |
+
import random
|
4 |
+
import re
|
5 |
+
import string
|
6 |
+
|
7 |
+
import gradio as gr
|
8 |
+
|
9 |
+
import openai
|
10 |
+
from icrawler import ImageDownloader
|
11 |
+
from icrawler.builtin import GoogleImageCrawler, BingImageCrawler
|
12 |
+
from uuid import uuid4
|
13 |
+
from pptx import Presentation
|
14 |
+
|
15 |
+
bad_coding_practice = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in
|
16 |
+
range(16))
|
17 |
+
|
18 |
+
|
19 |
+
def refresh_bad_coding_practice():
|
20 |
+
global bad_coding_practice
|
21 |
+
bad_coding_practice = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits)
|
22 |
+
for _ in range(16))
|
23 |
+
return
|
24 |
+
|
25 |
+
|
26 |
+
class PrefixNameDownloader(ImageDownloader):
|
27 |
+
|
28 |
+
def get_filename(self, task, default_ext):
|
29 |
+
filename = super(PrefixNameDownloader, self).get_filename(
|
30 |
+
task, default_ext)
|
31 |
+
print(bad_coding_practice)
|
32 |
+
return 'prefix_' + bad_coding_practice + filename
|
33 |
+
|
34 |
+
|
35 |
+
def generate_ppt(file, topic, slide_length, api_key):
|
36 |
+
print(file.name)
|
37 |
+
|
38 |
+
root = Presentation(file.name)
|
39 |
+
|
40 |
+
openai.api_key = api_key
|
41 |
+
|
42 |
+
message = f"""
|
43 |
+
Create content for a slideshow presentation.
|
44 |
+
The content's topic is {topic}.
|
45 |
+
The slideshow is {slide_length} slides long.
|
46 |
+
The content is written in the language of the content I give you above.
|
47 |
+
|
48 |
+
|
49 |
+
You are allowed to use the following slide types:
|
50 |
+
|
51 |
+
Slide types:
|
52 |
+
Title Slide - (Title, Subtitle)
|
53 |
+
Content Slide - (Title, Content)
|
54 |
+
Image Slide - (Title, Content, Image)
|
55 |
+
Thanks Slide - (Title)
|
56 |
+
|
57 |
+
Put this tag before the Title Slide: [L_TS]
|
58 |
+
Put this tag before the Content Slide: [L_CS]
|
59 |
+
Put this tag before the Image Slide: [L_IS]
|
60 |
+
Put this tag before the Thanks Slide: [L_THS]
|
61 |
+
|
62 |
+
Put "[SLIDEBREAK]" after each slide
|
63 |
+
|
64 |
+
For example:
|
65 |
+
[L_TS]
|
66 |
+
[TITLE]Mental Health[/TITLE]
|
67 |
+
|
68 |
+
[SLIDEBREAK]
|
69 |
+
|
70 |
+
[L_CS]
|
71 |
+
[TITLE]Mental Health Definition[/TITLE]
|
72 |
+
[CONTENT]
|
73 |
+
1. Definition: A person’s condition with regard to their psychological and emotional well-being
|
74 |
+
2. Can impact one's physical health
|
75 |
+
3. Stigmatized too often.
|
76 |
+
[/CONTENT]
|
77 |
+
|
78 |
+
[SLIDEBREAK]
|
79 |
+
|
80 |
+
Put this tag before the Title: [TITLE]
|
81 |
+
Put this tag after the Title: [/TITLE]
|
82 |
+
Put this tag before the Subitle: [SUBTITLE]
|
83 |
+
Put this tag after the Subtitle: [/SUBTITLE]
|
84 |
+
Put this tag before the Content: [CONTENT]
|
85 |
+
Put this tag after the Content: [/CONTENT]
|
86 |
+
Put this tag before the Image: [IMAGE]
|
87 |
+
Put this tag after the Image: [/IMAGE]
|
88 |
+
|
89 |
+
Elaborate on the Content, provide as much information as possible.
|
90 |
+
You put a [/CONTENT] at the end of the Content.
|
91 |
+
Do not reply as if you are talking about the slideshow itself. (ex. "Include pictures here about...")
|
92 |
+
Do not include any special characters (?, !, ., :, ) in the Title.
|
93 |
+
Do not include any additional information in your response and stick to the format."""
|
94 |
+
|
95 |
+
response = openai.ChatCompletion.create(
|
96 |
+
model="gpt-3.5-turbo",
|
97 |
+
messages=[
|
98 |
+
{"role": "user", "content": message}
|
99 |
+
]
|
100 |
+
)
|
101 |
+
|
102 |
+
# """ Ref for slide types:
|
103 |
+
# 0 -> title and subtitle
|
104 |
+
# 1 -> title and content
|
105 |
+
# 2 -> section header
|
106 |
+
# 3 -> two content
|
107 |
+
# 4 -> Comparison
|
108 |
+
# 5 -> Title only
|
109 |
+
# 6 -> Blank
|
110 |
+
# 7 -> Content with caption
|
111 |
+
# 8 -> Pic with caption
|
112 |
+
# """
|
113 |
+
|
114 |
+
def delete_all_slides():
|
115 |
+
for i in range(len(root.slides) - 1, -1, -1):
|
116 |
+
r_id = root.slides._sldIdLst[i].rId
|
117 |
+
root.part.drop_rel(r_id)
|
118 |
+
del root.slides._sldIdLst[i]
|
119 |
+
|
120 |
+
def create_title_slide(title, subtitle):
|
121 |
+
layout = root.slide_layouts[0]
|
122 |
+
slide = root.slides.add_slide(layout)
|
123 |
+
slide.shapes.title.text = title
|
124 |
+
slide.placeholders[1].text = subtitle
|
125 |
+
|
126 |
+
def create_section_header_slide(title):
|
127 |
+
layout = root.slide_layouts[2]
|
128 |
+
slide = root.slides.add_slide(layout)
|
129 |
+
slide.shapes.title.text = title
|
130 |
+
|
131 |
+
def create_title_and_content_slide(title, content):
|
132 |
+
layout = root.slide_layouts[1]
|
133 |
+
slide = root.slides.add_slide(layout)
|
134 |
+
slide.shapes.title.text = title
|
135 |
+
slide.placeholders[1].text = content
|
136 |
+
|
137 |
+
def create_title_and_content_and_image_slide(title, content, image_query):
|
138 |
+
layout = root.slide_layouts[8]
|
139 |
+
slide = root.slides.add_slide(layout)
|
140 |
+
slide.shapes.title.text = title
|
141 |
+
slide.placeholders[2].text = content
|
142 |
+
refresh_bad_coding_practice()
|
143 |
+
bing_crawler = GoogleImageCrawler(downloader_cls=PrefixNameDownloader, storage={'root_dir': os.getcwd()})
|
144 |
+
bing_crawler.crawl(keyword=image_query, max_num=1)
|
145 |
+
dir_path = os.path.dirname(os.path.realpath(__file__))
|
146 |
+
file_name = glob.glob(f"prefix_{bad_coding_practice}*")
|
147 |
+
print(file_name)
|
148 |
+
img_path = os.path.join(dir_path, file_name[0])
|
149 |
+
slide.shapes.add_picture(img_path, slide.placeholders[1].left, slide.placeholders[1].top,
|
150 |
+
slide.placeholders[1].width, slide.placeholders[1].height)
|
151 |
+
|
152 |
+
def find_text_in_between_tags(text, start_tag, end_tag):
|
153 |
+
start_pos = text.find(start_tag)
|
154 |
+
end_pos = text.find(end_tag)
|
155 |
+
result = []
|
156 |
+
while start_pos > -1 and end_pos > -1:
|
157 |
+
text_between_tags = text[start_pos + len(start_tag):end_pos]
|
158 |
+
result.append(text_between_tags)
|
159 |
+
start_pos = text.find(start_tag, end_pos + len(end_tag))
|
160 |
+
end_pos = text.find(end_tag, start_pos)
|
161 |
+
res1 = "".join(result)
|
162 |
+
res2 = re.sub(r"\[IMAGE\].*?\[/IMAGE\]", '', res1)
|
163 |
+
if len(result) > 0:
|
164 |
+
return res2
|
165 |
+
else:
|
166 |
+
return ""
|
167 |
+
|
168 |
+
def search_for_slide_type(text):
|
169 |
+
tags = ["[L_TS]", "[L_CS]", "[L_IS]", "[L_THS]"]
|
170 |
+
found_text = next((s for s in tags if s in text), None)
|
171 |
+
return found_text
|
172 |
+
|
173 |
+
def parse_response(reply):
|
174 |
+
list_of_slides = reply.split("[SLIDEBREAK]")
|
175 |
+
for slide in list_of_slides:
|
176 |
+
slide_type = search_for_slide_type(slide)
|
177 |
+
if slide_type == "[L_TS]":
|
178 |
+
create_title_slide(find_text_in_between_tags(str(slide), "[TITLE]", "[/TITLE]"),
|
179 |
+
find_text_in_between_tags(str(slide), "[SUBTITLE]", "[/SUBTITLE]"))
|
180 |
+
elif slide_type == "[L_CS]":
|
181 |
+
create_title_and_content_slide("".join(find_text_in_between_tags(str(slide), "[TITLE]", "[/TITLE]")),
|
182 |
+
"".join(find_text_in_between_tags(str(slide), "[CONTENT]",
|
183 |
+
"[/CONTENT]")))
|
184 |
+
elif slide_type == "[L_IS]":
|
185 |
+
create_title_and_content_and_image_slide("".join(find_text_in_between_tags(str(slide), "[TITLE]",
|
186 |
+
"[/TITLE]")),
|
187 |
+
"".join(find_text_in_between_tags(str(slide), "[CONTENT]",
|
188 |
+
"[/CONTENT]")),
|
189 |
+
"".join(find_text_in_between_tags(str(slide), "[IMAGE]",
|
190 |
+
"[/IMAGE]")))
|
191 |
+
elif slide_type == "[L_THS]":
|
192 |
+
create_section_header_slide("".join(find_text_in_between_tags(str(slide), "[TITLE]", "[/TITLE]")))
|
193 |
+
|
194 |
+
def find_title():
|
195 |
+
return root.slides[0].shapes.title.text
|
196 |
+
|
197 |
+
delete_all_slides()
|
198 |
+
|
199 |
+
print(response)
|
200 |
+
|
201 |
+
parse_response(response['choices'][0]['message']['content'])
|
202 |
+
|
203 |
+
name_ = str(uuid4()).replace('-', '')
|
204 |
+
|
205 |
+
root.save(f"./{name_}.pptx")
|
206 |
+
|
207 |
+
print("done")
|
208 |
+
|
209 |
+
dir_path = "./"
|
210 |
+
prefix = "prefix_"
|
211 |
+
|
212 |
+
for file_name in os.listdir(dir_path):
|
213 |
+
if file_name.startswith(prefix):
|
214 |
+
file_path = os.path.join(dir_path, file_name)
|
215 |
+
if os.path.isfile(file_path):
|
216 |
+
os.remove(file_path)
|
217 |
+
|
218 |
+
return f"./{name_}.pptx"
|
219 |
+
|
220 |
+
|
221 |
+
with gr.Blocks(title="ChatGPT PPT框架生成") as demo:
|
222 |
+
gr.Markdown("""<h1><center>ChatGPT PPT框架生成</center></h1>""")
|
223 |
+
with gr.Row():
|
224 |
+
with gr.Column():
|
225 |
+
openai_token = gr.Textbox(label="OpenAI API Key")
|
226 |
+
topic = gr.Textbox(label="PPT的主题或内容")
|
227 |
+
length = gr.Slider(minimum=1, maximum=20, value=6, label="生成的PPT页数", step=1)
|
228 |
+
theme = gr.File(value="./theme.pptx", file_types=['pptx', 'ppt'], label="PPT模版")
|
229 |
+
output_file = gr.File(interactive=False)
|
230 |
+
|
231 |
+
topic.submit(
|
232 |
+
fn=generate_ppt,
|
233 |
+
inputs=[theme, topic, length, openai_token],
|
234 |
+
outputs=[output_file]
|
235 |
+
)
|
236 |
+
|
237 |
+
submit = gr.Button("生成")
|
238 |
+
submit.click(
|
239 |
+
fn=generate_ppt,
|
240 |
+
inputs=[theme, topic, length, openai_token],
|
241 |
+
outputs=[output_file]
|
242 |
+
)
|
243 |
+
|
244 |
+
if __name__ == "__main__":
|
245 |
+
demo.launch()
|
requirements.txt
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
gradio==3.21.0
|
2 |
+
python-pptx==0.6.21
|
3 |
+
icrawler==0.6.6
|
4 |
+
openai==0.27.1
|
theme.pptx
ADDED
Binary file (276 kB). View file
|
|