Spaces:
Running
Running
Add a new template (with user-edited master slides); get slide placeholders and index properly for this new template
Browse files- .gitattributes +1 -0
- global_config.py +8 -4
- helpers/pptx_helper.py +215 -104
- pptx_templates/Minimalist_sales_pitch.pptx +3 -0
.gitattributes
CHANGED
@@ -34,3 +34,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
*.pptx filter=lfs diff=lfs merge=lfs -text
|
|
|
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
*.pptx filter=lfs diff=lfs merge=lfs -text
|
37 |
+
pptx_templates/Minimalist_sales_pitch.pptx filter=lfs diff=lfs merge=lfs -text
|
global_config.py
CHANGED
@@ -38,16 +38,20 @@ class GlobalConfig:
|
|
38 |
PPTX_TEMPLATE_FILES = {
|
39 |
'Basic': {
|
40 |
'file': 'pptx_templates/Blank.pptx',
|
41 |
-
'caption': 'A good start (Uses [photos](https://unsplash.com/photos/AFZ-qBPEceA) by [cetteup](https://unsplash.com/@cetteup?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash) on [Unsplash](https://unsplash.com/photos/a-foggy-forest-filled-with-lots-of-trees-d3ci37Gcgxg?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash))'
|
|
|
|
|
|
|
|
|
42 |
},
|
43 |
'Ion Boardroom': {
|
44 |
'file': 'pptx_templates/Ion_Boardroom.pptx',
|
45 |
-
'caption': 'Make some bold decisions'
|
46 |
},
|
47 |
'Urban Monochrome': {
|
48 |
'file': 'pptx_templates/Urban_monochrome.pptx',
|
49 |
-
'caption': 'Marvel in a monochrome dream'
|
50 |
-
}
|
51 |
}
|
52 |
|
53 |
# This is a long text, so not incorporated as a string in `strings.json`
|
|
|
38 |
PPTX_TEMPLATE_FILES = {
|
39 |
'Basic': {
|
40 |
'file': 'pptx_templates/Blank.pptx',
|
41 |
+
'caption': '🟠 A good start (Uses [photos](https://unsplash.com/photos/AFZ-qBPEceA) by [cetteup](https://unsplash.com/@cetteup?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash) on [Unsplash](https://unsplash.com/photos/a-foggy-forest-filled-with-lots-of-trees-d3ci37Gcgxg?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash))'
|
42 |
+
},
|
43 |
+
'Minimalist Sales Pitch': {
|
44 |
+
'file': 'pptx_templates/Minimalist_sales_pitch.pptx',
|
45 |
+
'caption': '⚫ In high contrast'
|
46 |
},
|
47 |
'Ion Boardroom': {
|
48 |
'file': 'pptx_templates/Ion_Boardroom.pptx',
|
49 |
+
'caption': '🔴 Make some bold decisions'
|
50 |
},
|
51 |
'Urban Monochrome': {
|
52 |
'file': 'pptx_templates/Urban_monochrome.pptx',
|
53 |
+
'caption': '⚪ Marvel in a monochrome dream'
|
54 |
+
},
|
55 |
}
|
56 |
|
57 |
# This is a long text, so not incorporated as a string in `strings.json`
|
helpers/pptx_helper.py
CHANGED
@@ -73,11 +73,6 @@ def generate_powerpoint_presentation(
|
|
73 |
|
74 |
# The structured "JSON" might contain trailing commas, so using json5
|
75 |
parsed_data = json5.loads(structured_data)
|
76 |
-
|
77 |
-
logger.debug(
|
78 |
-
'*** Using PPTX template: %s',
|
79 |
-
GlobalConfig.PPTX_TEMPLATE_FILES[slides_template]['file']
|
80 |
-
)
|
81 |
presentation = pptx.Presentation(GlobalConfig.PPTX_TEMPLATE_FILES[slides_template]['file'])
|
82 |
slide_width_inch, slide_height_inch = _get_slide_width_height_inches(presentation)
|
83 |
|
@@ -88,14 +83,17 @@ def generate_powerpoint_presentation(
|
|
88 |
subtitle = slide.placeholders[1]
|
89 |
title.text = parsed_data['title']
|
90 |
logger.info(
|
91 |
-
'PPT title: %s | #slides: %d',
|
92 |
-
title.text, len(parsed_data['slides'])
|
|
|
93 |
)
|
94 |
subtitle.text = 'by Myself and SlideDeck AI :)'
|
95 |
all_headers = [title.text, ]
|
96 |
|
97 |
# Add content in a loop
|
98 |
for a_slide in parsed_data['slides']:
|
|
|
|
|
99 |
is_processing_done = _handle_double_col_layout(
|
100 |
presentation=presentation,
|
101 |
slide_json=a_slide,
|
@@ -151,6 +149,47 @@ def get_flat_list_of_contents(items: list, level: int) -> List[Tuple]:
|
|
151 |
return flat_list
|
152 |
|
153 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
def _handle_default_display(
|
155 |
presentation: pptx.Presentation,
|
156 |
slide_json: dict,
|
@@ -193,7 +232,13 @@ def _handle_default_display(
|
|
193 |
|
194 |
shapes = slide.shapes
|
195 |
title_shape = shapes.title
|
196 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
title_shape.text = remove_slide_number_from_heading(slide_json['heading'])
|
198 |
text_frame = body_shape.text_frame
|
199 |
|
@@ -238,12 +283,31 @@ def _handle_display_image__in_foreground(
|
|
238 |
img_keywords = slide_json['img_keywords'].strip()
|
239 |
slide = presentation.slide_layouts[8] # Picture with Caption
|
240 |
slide = presentation.slides.add_slide(slide)
|
|
|
241 |
|
242 |
title_placeholder = slide.shapes.title
|
243 |
title_placeholder.text = remove_slide_number_from_heading(slide_json['heading'])
|
244 |
|
245 |
-
|
246 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
247 |
flat_items_list = get_flat_list_of_contents(slide_json['bullet_points'], level=0)
|
248 |
|
249 |
for idx, an_item in enumerate(flat_items_list):
|
@@ -305,9 +369,15 @@ def _handle_display_image__in_background(
|
|
305 |
|
306 |
# Add a photo in the background, text in the foreground
|
307 |
slide = presentation.slides.add_slide(presentation.slide_layouts[1])
|
308 |
-
|
309 |
title_shape = slide.shapes.title
|
310 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
311 |
title_shape.text = remove_slide_number_from_heading(slide_json['heading'])
|
312 |
|
313 |
flat_items_list = get_flat_list_of_contents(slide_json['bullet_points'], level=0)
|
@@ -418,39 +488,73 @@ def _handle_double_col_layout(
|
|
418 |
) and isinstance(double_col_content[0], dict) and isinstance(double_col_content[1], dict):
|
419 |
slide = presentation.slide_layouts[4]
|
420 |
slide = presentation.slides.add_slide(slide)
|
|
|
421 |
|
422 |
shapes = slide.shapes
|
423 |
title_placeholder = shapes.title
|
424 |
title_placeholder.text = remove_slide_number_from_heading(slide_json['heading'])
|
425 |
|
426 |
-
|
427 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
428 |
left_col_frame, right_col_frame = left_col.text_frame, right_col.text_frame
|
429 |
|
430 |
-
if 'heading' in double_col_content[0]:
|
431 |
left_heading.text = double_col_content[0]['heading']
|
432 |
if 'bullet_points' in double_col_content[0]:
|
433 |
flat_items_list = get_flat_list_of_contents(
|
434 |
double_col_content[0]['bullet_points'], level=0
|
435 |
)
|
436 |
|
|
|
|
|
|
|
437 |
for idx, an_item in enumerate(flat_items_list):
|
438 |
-
if idx == 0:
|
439 |
left_col_frame.text = an_item[0].removeprefix(STEP_BY_STEP_PROCESS_MARKER)
|
440 |
else:
|
441 |
paragraph = left_col_frame.add_paragraph()
|
442 |
paragraph.text = an_item[0].removeprefix(STEP_BY_STEP_PROCESS_MARKER)
|
443 |
paragraph.level = an_item[1]
|
444 |
|
445 |
-
if 'heading' in double_col_content[1]:
|
446 |
right_heading.text = double_col_content[1]['heading']
|
447 |
if 'bullet_points' in double_col_content[1]:
|
448 |
flat_items_list = get_flat_list_of_contents(
|
449 |
double_col_content[1]['bullet_points'], level=0
|
450 |
)
|
451 |
|
|
|
|
|
|
|
452 |
for idx, an_item in enumerate(flat_items_list):
|
453 |
-
if idx == 0:
|
454 |
right_col_frame.text = an_item[0].removeprefix(STEP_BY_STEP_PROCESS_MARKER)
|
455 |
else:
|
456 |
paragraph = right_col_frame.add_paragraph()
|
@@ -614,153 +718,160 @@ def _get_slide_width_height_inches(presentation: pptx.Presentation) -> Tuple[flo
|
|
614 |
return slide_width_inch, slide_height_inch
|
615 |
|
616 |
|
617 |
-
def print_placeholder_names(slide: pptx.slide.Slide):
|
618 |
-
"""
|
619 |
-
Display the placeholder details of a given slide.
|
620 |
-
|
621 |
-
:param slide: The slide.
|
622 |
-
"""
|
623 |
-
|
624 |
-
for shape in slide.placeholders:
|
625 |
-
print(f'{shape.placeholder_format.idx=}, {shape.name=}')
|
626 |
-
|
627 |
-
|
628 |
if __name__ == '__main__':
|
629 |
_JSON_DATA = '''
|
630 |
{
|
631 |
-
"title": "
|
632 |
"slides": [
|
633 |
{
|
634 |
-
"heading": "Introduction to
|
635 |
"bullet_points": [
|
636 |
-
"
|
637 |
-
|
638 |
-
|
|
|
|
|
|
|
639 |
],
|
640 |
-
"key_message": "",
|
641 |
-
"img_keywords": "
|
642 |
},
|
643 |
{
|
644 |
-
"heading": "
|
645 |
"bullet_points": [
|
646 |
-
"
|
647 |
-
"
|
648 |
-
|
649 |
-
"Squares: Special type of rectangle with equal sides",
|
650 |
-
"Rounded Rectangles: Rectangles with rounded corners"
|
651 |
-
],
|
652 |
-
"Circles: Round shapes with no corners",
|
653 |
-
"Ovals: Elliptical shapes"
|
654 |
],
|
655 |
-
"key_message": "",
|
656 |
-
"img_keywords": "
|
657 |
},
|
658 |
{
|
659 |
-
"heading": "
|
660 |
"bullet_points": [
|
661 |
-
">>
|
662 |
-
">>
|
663 |
-
">>
|
664 |
-
">>
|
665 |
-
">>
|
|
|
|
|
|
|
666 |
],
|
667 |
-
"key_message": "
|
668 |
-
"img_keywords": "
|
669 |
},
|
670 |
{
|
671 |
-
"heading": "
|
672 |
"bullet_points": [
|
673 |
{
|
674 |
-
"heading": "
|
675 |
"bullet_points": [
|
676 |
-
"
|
677 |
-
"
|
678 |
]
|
679 |
},
|
680 |
{
|
681 |
-
"heading": "
|
682 |
"bullet_points": [
|
683 |
-
"
|
684 |
-
"
|
685 |
]
|
686 |
}
|
687 |
],
|
688 |
-
"key_message": "
|
689 |
-
"img_keywords": "
|
690 |
},
|
691 |
{
|
692 |
-
"heading": "
|
693 |
"bullet_points": [
|
694 |
-
"
|
695 |
-
"
|
696 |
-
"
|
697 |
],
|
698 |
-
"key_message": "",
|
699 |
-
"img_keywords": "
|
700 |
},
|
701 |
{
|
702 |
-
"heading": "
|
703 |
"bullet_points": [
|
704 |
-
"
|
705 |
-
"
|
706 |
-
"
|
|
|
|
|
707 |
],
|
708 |
-
"key_message": "
|
709 |
-
"img_keywords": "
|
710 |
},
|
711 |
{
|
712 |
-
"heading": "
|
713 |
"bullet_points": [
|
714 |
-
"
|
715 |
-
"
|
716 |
-
"
|
|
|
717 |
],
|
718 |
-
"key_message": "
|
719 |
-
"img_keywords": "
|
720 |
},
|
721 |
{
|
722 |
-
"heading": "
|
723 |
"bullet_points": [
|
724 |
{
|
725 |
-
"heading": "
|
726 |
"bullet_points": [
|
727 |
-
"
|
728 |
-
"
|
|
|
729 |
]
|
730 |
},
|
731 |
{
|
732 |
-
"heading": "
|
733 |
"bullet_points": [
|
734 |
-
"
|
735 |
-
"
|
|
|
736 |
]
|
737 |
}
|
738 |
],
|
739 |
-
"key_message": "
|
740 |
-
"img_keywords": "
|
741 |
},
|
742 |
{
|
743 |
-
"heading": "
|
744 |
"bullet_points": [
|
745 |
-
|
746 |
-
|
747 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
748 |
],
|
749 |
-
"key_message": "
|
750 |
-
"img_keywords": "
|
751 |
},
|
752 |
{
|
753 |
"heading": "Conclusion",
|
754 |
"bullet_points": [
|
755 |
-
"
|
756 |
-
"
|
757 |
-
"Practice
|
758 |
],
|
759 |
-
"key_message": "
|
760 |
-
"img_keywords": "
|
761 |
}
|
762 |
]
|
763 |
-
}
|
|
|
764 |
|
765 |
temp = tempfile.NamedTemporaryFile(delete=False, suffix='.pptx')
|
766 |
path = pathlib.Path(temp.name)
|
@@ -768,7 +879,7 @@ if __name__ == '__main__':
|
|
768 |
generate_powerpoint_presentation(
|
769 |
json5.loads(_JSON_DATA),
|
770 |
output_file_path=path,
|
771 |
-
slides_template='
|
772 |
)
|
773 |
print(f'File path: {path}')
|
774 |
|
|
|
73 |
|
74 |
# The structured "JSON" might contain trailing commas, so using json5
|
75 |
parsed_data = json5.loads(structured_data)
|
|
|
|
|
|
|
|
|
|
|
76 |
presentation = pptx.Presentation(GlobalConfig.PPTX_TEMPLATE_FILES[slides_template]['file'])
|
77 |
slide_width_inch, slide_height_inch = _get_slide_width_height_inches(presentation)
|
78 |
|
|
|
83 |
subtitle = slide.placeholders[1]
|
84 |
title.text = parsed_data['title']
|
85 |
logger.info(
|
86 |
+
'PPT title: %s | #slides: %d | template: %s',
|
87 |
+
title.text, len(parsed_data['slides']),
|
88 |
+
GlobalConfig.PPTX_TEMPLATE_FILES[slides_template]['file']
|
89 |
)
|
90 |
subtitle.text = 'by Myself and SlideDeck AI :)'
|
91 |
all_headers = [title.text, ]
|
92 |
|
93 |
# Add content in a loop
|
94 |
for a_slide in parsed_data['slides']:
|
95 |
+
# The loop has a bug:
|
96 |
+
# if any of this functions fail (i.e., returns False), an empty slide would still exist
|
97 |
is_processing_done = _handle_double_col_layout(
|
98 |
presentation=presentation,
|
99 |
slide_json=a_slide,
|
|
|
149 |
return flat_list
|
150 |
|
151 |
|
152 |
+
def get_slide_placeholders(
|
153 |
+
slide: pptx.slide.Slide,
|
154 |
+
layout_number: int,
|
155 |
+
is_debug: bool = False
|
156 |
+
) -> List[Tuple[int, str]]:
|
157 |
+
"""
|
158 |
+
Return the index and name (lower case) of all placeholders present in a slide, except
|
159 |
+
the title placeholder.
|
160 |
+
|
161 |
+
A placeholder in a slide is a place to add content. Each placeholder has a name and an index.
|
162 |
+
This index is NOT a list index, rather a set of keys used to look up a dict. So, `idx` is
|
163 |
+
non-contiguous. Also, the title placeholder of a slide always has index 0. User-added
|
164 |
+
placeholder get indices assigned starting from 10.
|
165 |
+
|
166 |
+
With user-edited or added placeholders, their index may be difficult to track. This function
|
167 |
+
returns the placeholders name as well, which could be useful to distinguish between the
|
168 |
+
different placeholder.
|
169 |
+
|
170 |
+
:param slide: The slide.
|
171 |
+
:param layout_number: The layout number used by the slide.
|
172 |
+
:param is_debug: Whether to print debugging statements.
|
173 |
+
:return: A list containing placeholders (idx, name) tuples, except the title placeholder.
|
174 |
+
"""
|
175 |
+
|
176 |
+
if is_debug:
|
177 |
+
print(
|
178 |
+
f'Slide layout #{layout_number}:'
|
179 |
+
f' # of placeholders: {len(slide.shapes.placeholders)} (including the title)'
|
180 |
+
)
|
181 |
+
|
182 |
+
placeholders = [
|
183 |
+
(shape.placeholder_format.idx, shape.name.lower()) for shape in slide.shapes.placeholders
|
184 |
+
]
|
185 |
+
placeholders.pop(0) # Remove the title placeholder
|
186 |
+
|
187 |
+
if is_debug:
|
188 |
+
print(placeholders)
|
189 |
+
|
190 |
+
return placeholders
|
191 |
+
|
192 |
+
|
193 |
def _handle_default_display(
|
194 |
presentation: pptx.Presentation,
|
195 |
slide_json: dict,
|
|
|
232 |
|
233 |
shapes = slide.shapes
|
234 |
title_shape = shapes.title
|
235 |
+
|
236 |
+
try:
|
237 |
+
body_shape = shapes.placeholders[1]
|
238 |
+
except KeyError:
|
239 |
+
placeholders = get_slide_placeholders(slide, layout_number=1)
|
240 |
+
body_shape = shapes.placeholders[placeholders[0][0]]
|
241 |
+
|
242 |
title_shape.text = remove_slide_number_from_heading(slide_json['heading'])
|
243 |
text_frame = body_shape.text_frame
|
244 |
|
|
|
283 |
img_keywords = slide_json['img_keywords'].strip()
|
284 |
slide = presentation.slide_layouts[8] # Picture with Caption
|
285 |
slide = presentation.slides.add_slide(slide)
|
286 |
+
placeholders = None
|
287 |
|
288 |
title_placeholder = slide.shapes.title
|
289 |
title_placeholder.text = remove_slide_number_from_heading(slide_json['heading'])
|
290 |
|
291 |
+
try:
|
292 |
+
pic_col: PicturePlaceholder = slide.shapes.placeholders[1]
|
293 |
+
except KeyError:
|
294 |
+
placeholders = get_slide_placeholders(slide, layout_number=8)
|
295 |
+
pic_col = None
|
296 |
+
for idx, name in placeholders:
|
297 |
+
if 'picture' in name:
|
298 |
+
pic_col: PicturePlaceholder = slide.shapes.placeholders[idx]
|
299 |
+
|
300 |
+
try:
|
301 |
+
text_col: SlidePlaceholder = slide.shapes.placeholders[2]
|
302 |
+
except KeyError:
|
303 |
+
text_col = None
|
304 |
+
if not placeholders:
|
305 |
+
placeholders = get_slide_placeholders(slide, layout_number=8)
|
306 |
+
|
307 |
+
for idx, name in placeholders:
|
308 |
+
if 'content' in name:
|
309 |
+
text_col: SlidePlaceholder = slide.shapes.placeholders[idx]
|
310 |
+
|
311 |
flat_items_list = get_flat_list_of_contents(slide_json['bullet_points'], level=0)
|
312 |
|
313 |
for idx, an_item in enumerate(flat_items_list):
|
|
|
369 |
|
370 |
# Add a photo in the background, text in the foreground
|
371 |
slide = presentation.slides.add_slide(presentation.slide_layouts[1])
|
|
|
372 |
title_shape = slide.shapes.title
|
373 |
+
|
374 |
+
try:
|
375 |
+
body_shape = slide.shapes.placeholders[1]
|
376 |
+
except KeyError:
|
377 |
+
placeholders = get_slide_placeholders(slide, layout_number=1)
|
378 |
+
# Layout 1 usually has two placeholders, including the title
|
379 |
+
body_shape = slide.shapes.placeholders[placeholders[0][0]]
|
380 |
+
|
381 |
title_shape.text = remove_slide_number_from_heading(slide_json['heading'])
|
382 |
|
383 |
flat_items_list = get_flat_list_of_contents(slide_json['bullet_points'], level=0)
|
|
|
488 |
) and isinstance(double_col_content[0], dict) and isinstance(double_col_content[1], dict):
|
489 |
slide = presentation.slide_layouts[4]
|
490 |
slide = presentation.slides.add_slide(slide)
|
491 |
+
placeholders = None
|
492 |
|
493 |
shapes = slide.shapes
|
494 |
title_placeholder = shapes.title
|
495 |
title_placeholder.text = remove_slide_number_from_heading(slide_json['heading'])
|
496 |
|
497 |
+
try:
|
498 |
+
left_heading, right_heading = shapes.placeholders[1], shapes.placeholders[3]
|
499 |
+
except KeyError:
|
500 |
+
# For manually edited/added master slides, the placeholder idx numbers in the dict
|
501 |
+
# will be different (>= 10)
|
502 |
+
left_heading, right_heading = None, None
|
503 |
+
placeholders = get_slide_placeholders(slide, layout_number=4)
|
504 |
+
|
505 |
+
for idx, name in placeholders:
|
506 |
+
if 'text placeholder' in name:
|
507 |
+
if not left_heading:
|
508 |
+
left_heading = shapes.placeholders[idx]
|
509 |
+
elif not right_heading:
|
510 |
+
right_heading = shapes.placeholders[idx]
|
511 |
+
|
512 |
+
try:
|
513 |
+
left_col, right_col = shapes.placeholders[2], shapes.placeholders[4]
|
514 |
+
except KeyError:
|
515 |
+
left_col, right_col = None, None
|
516 |
+
if not placeholders:
|
517 |
+
placeholders = get_slide_placeholders(slide, layout_number=4)
|
518 |
+
|
519 |
+
for idx, name in placeholders:
|
520 |
+
if 'content placeholder' in name:
|
521 |
+
if not left_col:
|
522 |
+
left_col = shapes.placeholders[idx]
|
523 |
+
elif not right_col:
|
524 |
+
right_col = shapes.placeholders[idx]
|
525 |
+
|
526 |
left_col_frame, right_col_frame = left_col.text_frame, right_col.text_frame
|
527 |
|
528 |
+
if 'heading' in double_col_content[0] and left_heading:
|
529 |
left_heading.text = double_col_content[0]['heading']
|
530 |
if 'bullet_points' in double_col_content[0]:
|
531 |
flat_items_list = get_flat_list_of_contents(
|
532 |
double_col_content[0]['bullet_points'], level=0
|
533 |
)
|
534 |
|
535 |
+
if not left_heading:
|
536 |
+
left_col_frame.text = double_col_content[0]['heading']
|
537 |
+
|
538 |
for idx, an_item in enumerate(flat_items_list):
|
539 |
+
if left_heading and idx == 0:
|
540 |
left_col_frame.text = an_item[0].removeprefix(STEP_BY_STEP_PROCESS_MARKER)
|
541 |
else:
|
542 |
paragraph = left_col_frame.add_paragraph()
|
543 |
paragraph.text = an_item[0].removeprefix(STEP_BY_STEP_PROCESS_MARKER)
|
544 |
paragraph.level = an_item[1]
|
545 |
|
546 |
+
if 'heading' in double_col_content[1] and right_heading:
|
547 |
right_heading.text = double_col_content[1]['heading']
|
548 |
if 'bullet_points' in double_col_content[1]:
|
549 |
flat_items_list = get_flat_list_of_contents(
|
550 |
double_col_content[1]['bullet_points'], level=0
|
551 |
)
|
552 |
|
553 |
+
if not right_heading:
|
554 |
+
right_col_frame.text = double_col_content[1]['heading']
|
555 |
+
|
556 |
for idx, an_item in enumerate(flat_items_list):
|
557 |
+
if right_col_frame and idx == 0:
|
558 |
right_col_frame.text = an_item[0].removeprefix(STEP_BY_STEP_PROCESS_MARKER)
|
559 |
else:
|
560 |
paragraph = right_col_frame.add_paragraph()
|
|
|
718 |
return slide_width_inch, slide_height_inch
|
719 |
|
720 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
721 |
if __name__ == '__main__':
|
722 |
_JSON_DATA = '''
|
723 |
{
|
724 |
+
"title": "The Fascinating World of Chess",
|
725 |
"slides": [
|
726 |
{
|
727 |
+
"heading": "Introduction to Chess",
|
728 |
"bullet_points": [
|
729 |
+
"Chess is a strategic board game played between two players.",
|
730 |
+
[
|
731 |
+
"Each player begins the game with 16 pieces: one king, one queen, two rooks, two knights, two bishops, and eight pawns.",
|
732 |
+
"The goal of the game is to checkmate your opponent's king. This means the king is in a position to be captured (in 'check') but has no move to escape (mate)."
|
733 |
+
],
|
734 |
+
"Chess is believed to have originated in northern India in the 6th century AD."
|
735 |
],
|
736 |
+
"key_message": "Understanding the basics of chess is crucial before delving into strategies.",
|
737 |
+
"img_keywords": "chessboard, chess pieces, king, queen, rook, knight, bishop, pawn"
|
738 |
},
|
739 |
{
|
740 |
+
"heading": "The Chessboard",
|
741 |
"bullet_points": [
|
742 |
+
"The chessboard is made up of 64 squares in an 8x8 grid.",
|
743 |
+
"Each player starts with their pieces on their home rank (row).",
|
744 |
+
"The board is divided into two camps: one for each player."
|
|
|
|
|
|
|
|
|
|
|
745 |
],
|
746 |
+
"key_message": "Knowing the layout of the chessboard is essential for understanding piece movement.",
|
747 |
+
"img_keywords": "chessboard layout, 8x8 grid, home rank, player camps"
|
748 |
},
|
749 |
{
|
750 |
+
"heading": "Movement of Pieces",
|
751 |
"bullet_points": [
|
752 |
+
">> Each piece moves differently. Learning these movements is key to playing chess.",
|
753 |
+
">> The king moves one square in any direction.",
|
754 |
+
">> The queen combines the moves of the rook and bishop.",
|
755 |
+
">> The rook moves horizontally or vertically along a rank or file.",
|
756 |
+
">> The bishop moves diagonally.",
|
757 |
+
">> The knight moves in an L-shape: two squares in a horizontal or vertical direction, then one square perpendicular to that.",
|
758 |
+
">> The pawn moves forward one square, but captures diagonally.",
|
759 |
+
">> Pawns have the initial option of moving two squares forward on their first move."
|
760 |
],
|
761 |
+
"key_message": "Understanding how each piece moves is fundamental to playing chess.",
|
762 |
+
"img_keywords": "chess piece movements, king, queen, rook, bishop, knight, pawn"
|
763 |
},
|
764 |
{
|
765 |
+
"heading": "Special Moves",
|
766 |
"bullet_points": [
|
767 |
{
|
768 |
+
"heading": "Castling",
|
769 |
"bullet_points": [
|
770 |
+
"Castling is a unique move involving the king and a rook.",
|
771 |
+
"It involves moving the king two squares towards a rook, then moving that rook to the square the king skipped over."
|
772 |
]
|
773 |
},
|
774 |
{
|
775 |
+
"heading": "En Passant",
|
776 |
"bullet_points": [
|
777 |
+
"En passant is a special pawn capture move.",
|
778 |
+
"It occurs when a pawn moves two squares forward from its starting position and lands beside an opponent's pawn, which could have captured it if the first pawn had only moved one square forward."
|
779 |
]
|
780 |
}
|
781 |
],
|
782 |
+
"key_message": "Understanding these special moves can add depth to your chess strategy.",
|
783 |
+
"img_keywords": "castling, en passant, special chess moves"
|
784 |
},
|
785 |
{
|
786 |
+
"heading": "Chess Notation",
|
787 |
"bullet_points": [
|
788 |
+
"Chess notation is a system used to record and communicate chess games.",
|
789 |
+
"It uses algebraic notation, where each square on the board is identified by a letter and a number.",
|
790 |
+
"Pieces are identified by their initial letters: K for king, Q for queen, R for rook, B for bishop, N for knight, and P for pawn."
|
791 |
],
|
792 |
+
"key_message": "Learning chess notation is helpful for recording, analyzing, and discussing games.",
|
793 |
+
"img_keywords": "chess notation, algebraic notation, chess symbols"
|
794 |
},
|
795 |
{
|
796 |
+
"heading": "Chess Strategies",
|
797 |
"bullet_points": [
|
798 |
+
"Develop your pieces quickly and efficiently.",
|
799 |
+
"Control the center of the board.",
|
800 |
+
"Castle early to protect your king.",
|
801 |
+
"Keep your king safe.",
|
802 |
+
"Think ahead and plan your moves."
|
803 |
],
|
804 |
+
"key_message": "Following these strategies can help improve your chess skills.",
|
805 |
+
"img_keywords": "chess strategies, piece development, center control, king safety, planning ahead"
|
806 |
},
|
807 |
{
|
808 |
+
"heading": "Chess Tactics",
|
809 |
"bullet_points": [
|
810 |
+
"Fork: attacking two enemy pieces with the same move.",
|
811 |
+
"Pin: restricting the movement of an enemy piece.",
|
812 |
+
"Skewer: forcing an enemy piece to move away from a threatened piece.",
|
813 |
+
"Discovered attack: moving a piece to reveal an attack by another piece behind it."
|
814 |
],
|
815 |
+
"key_message": "Mastering these tactics can help you gain an advantage in games.",
|
816 |
+
"img_keywords": "chess tactics, fork, pin, skewer, discovered attack"
|
817 |
},
|
818 |
{
|
819 |
+
"heading": "Chess Openings",
|
820 |
"bullet_points": [
|
821 |
{
|
822 |
+
"heading": "Italian Game",
|
823 |
"bullet_points": [
|
824 |
+
"1. e4 e5",
|
825 |
+
"2. Nf3 Nc6",
|
826 |
+
"3. Bc4 Bc5"
|
827 |
]
|
828 |
},
|
829 |
{
|
830 |
+
"heading": "Ruy Lopez",
|
831 |
"bullet_points": [
|
832 |
+
"1. e4 e5",
|
833 |
+
"2. Nf3 Nc6",
|
834 |
+
"3. Bb5"
|
835 |
]
|
836 |
}
|
837 |
],
|
838 |
+
"key_message": "Learning popular chess openings can help you start games effectively.",
|
839 |
+
"img_keywords": "chess openings, Italian Game, Ruy Lopez"
|
840 |
},
|
841 |
{
|
842 |
+
"heading": "Chess Endgames",
|
843 |
"bullet_points": [
|
844 |
+
{
|
845 |
+
"heading": "King and Pawn Endgame",
|
846 |
+
"bullet_points": [
|
847 |
+
"This endgame involves a king and one or more pawns against a lone king.",
|
848 |
+
"The goal is to promote a pawn to a new queen."
|
849 |
+
]
|
850 |
+
},
|
851 |
+
{
|
852 |
+
"heading": "Rook Endgame",
|
853 |
+
"bullet_points": [
|
854 |
+
"This endgame involves a rook against a lone king.",
|
855 |
+
"The goal is to checkmate the opponent's king using the rook."
|
856 |
+
]
|
857 |
+
}
|
858 |
],
|
859 |
+
"key_message": "Understanding common chess endgames can help you win games.",
|
860 |
+
"img_keywords": "chess endgames, king and pawn endgame, rook endgame"
|
861 |
},
|
862 |
{
|
863 |
"heading": "Conclusion",
|
864 |
"bullet_points": [
|
865 |
+
"Chess is a complex game that requires strategy, tactics, and planning.",
|
866 |
+
"Understanding the rules, piece movements, and common strategies can help improve your chess skills.",
|
867 |
+
"Practice regularly to improve your game."
|
868 |
],
|
869 |
+
"key_message": "To excel at chess, one must understand its fundamentals and practice regularly.",
|
870 |
+
"img_keywords": "chess fundamentals, chess improvement, regular practice"
|
871 |
}
|
872 |
]
|
873 |
+
}
|
874 |
+
'''
|
875 |
|
876 |
temp = tempfile.NamedTemporaryFile(delete=False, suffix='.pptx')
|
877 |
path = pathlib.Path(temp.name)
|
|
|
879 |
generate_powerpoint_presentation(
|
880 |
json5.loads(_JSON_DATA),
|
881 |
output_file_path=path,
|
882 |
+
slides_template='Minimalist Sales Pitch'
|
883 |
)
|
884 |
print(f'File path: {path}')
|
885 |
|
pptx_templates/Minimalist_sales_pitch.pptx
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:2a6da48abf9a44bde11fa8337519435d51fe5f161e605d8f5ab00d4a914fc964
|
3 |
+
size 1298028
|