Spaces:
Running
Running
Fix #29: improve JSON handling and keys
Browse files- app.py +31 -20
- helpers/pptx_helper.py +32 -24
app.py
CHANGED
@@ -7,7 +7,7 @@ import os
|
|
7 |
import pathlib
|
8 |
import random
|
9 |
import tempfile
|
10 |
-
from typing import List
|
11 |
|
12 |
import json5
|
13 |
import streamlit as st
|
@@ -240,7 +240,9 @@ def set_up_chat_ui():
|
|
240 |
progress_bar.progress(1.0, text='Done!')
|
241 |
|
242 |
st.chat_message('ai').code(response, language='json')
|
243 |
-
|
|
|
|
|
244 |
|
245 |
logger.info(
|
246 |
'#messages in history / 2: %d',
|
@@ -248,15 +250,38 @@ def set_up_chat_ui():
|
|
248 |
)
|
249 |
|
250 |
|
251 |
-
def generate_slide_deck(json_str: str) -> pathlib.Path:
|
252 |
"""
|
253 |
Create a slide deck and return the file path. In case there is any error creating the slide
|
254 |
deck, the path may be to an empty file.
|
255 |
|
256 |
:param json_str: The content in *valid* JSON format.
|
257 |
-
:return: The
|
258 |
"""
|
259 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
260 |
if DOWNLOAD_FILE_KEY in st.session_state:
|
261 |
path = pathlib.Path(st.session_state[DOWNLOAD_FILE_KEY])
|
262 |
else:
|
@@ -267,24 +292,10 @@ def generate_slide_deck(json_str: str) -> pathlib.Path:
|
|
267 |
if temp:
|
268 |
temp.close()
|
269 |
|
270 |
-
logger.debug('Creating PPTX file: %s...', st.session_state[DOWNLOAD_FILE_KEY])
|
271 |
-
|
272 |
try:
|
|
|
273 |
pptx_helper.generate_powerpoint_presentation(
|
274 |
-
|
275 |
-
slides_template=pptx_template,
|
276 |
-
output_file_path=path
|
277 |
-
)
|
278 |
-
except ValueError:
|
279 |
-
st.error(
|
280 |
-
'Encountered error while parsing JSON...will fix it and retry'
|
281 |
-
)
|
282 |
-
logger.error(
|
283 |
-
'Caught ValueError: trying again after repairing JSON...'
|
284 |
-
)
|
285 |
-
|
286 |
-
pptx_helper.generate_powerpoint_presentation(
|
287 |
-
text_helper.fix_malformed_json(json_str),
|
288 |
slides_template=pptx_template,
|
289 |
output_file_path=path
|
290 |
)
|
|
|
7 |
import pathlib
|
8 |
import random
|
9 |
import tempfile
|
10 |
+
from typing import List, Union
|
11 |
|
12 |
import json5
|
13 |
import streamlit as st
|
|
|
240 |
progress_bar.progress(1.0, text='Done!')
|
241 |
|
242 |
st.chat_message('ai').code(response, language='json')
|
243 |
+
|
244 |
+
if path:
|
245 |
+
_display_download_button(path)
|
246 |
|
247 |
logger.info(
|
248 |
'#messages in history / 2: %d',
|
|
|
250 |
)
|
251 |
|
252 |
|
253 |
+
def generate_slide_deck(json_str: str) -> Union[pathlib.Path, None]:
|
254 |
"""
|
255 |
Create a slide deck and return the file path. In case there is any error creating the slide
|
256 |
deck, the path may be to an empty file.
|
257 |
|
258 |
:param json_str: The content in *valid* JSON format.
|
259 |
+
:return: The path to the .pptx file or `None` in case of error.
|
260 |
"""
|
261 |
|
262 |
+
try:
|
263 |
+
parsed_data = json5.loads(json_str)
|
264 |
+
except ValueError:
|
265 |
+
st.error(
|
266 |
+
'Encountered error while parsing JSON...will fix it and retry'
|
267 |
+
)
|
268 |
+
logger.error(
|
269 |
+
'Caught ValueError: trying again after repairing JSON...'
|
270 |
+
)
|
271 |
+
try:
|
272 |
+
parsed_data = json5.loads(text_helper.fix_malformed_json(json_str))
|
273 |
+
except ValueError:
|
274 |
+
st.error(
|
275 |
+
'Encountered an error again while fixing JSON...'
|
276 |
+
'the slide deck cannot be created, unfortunately ☹'
|
277 |
+
'\nPlease try again later.'
|
278 |
+
)
|
279 |
+
logger.error(
|
280 |
+
'Caught ValueError: failed to repair JSON!'
|
281 |
+
)
|
282 |
+
|
283 |
+
return None
|
284 |
+
|
285 |
if DOWNLOAD_FILE_KEY in st.session_state:
|
286 |
path = pathlib.Path(st.session_state[DOWNLOAD_FILE_KEY])
|
287 |
else:
|
|
|
292 |
if temp:
|
293 |
temp.close()
|
294 |
|
|
|
|
|
295 |
try:
|
296 |
+
logger.debug('Creating PPTX file: %s...', st.session_state[DOWNLOAD_FILE_KEY])
|
297 |
pptx_helper.generate_powerpoint_presentation(
|
298 |
+
parsed_data,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
299 |
slides_template=pptx_template,
|
300 |
output_file_path=path
|
301 |
)
|
helpers/pptx_helper.py
CHANGED
@@ -82,21 +82,19 @@ def remove_slide_number_from_heading(header: str) -> str:
|
|
82 |
|
83 |
|
84 |
def generate_powerpoint_presentation(
|
85 |
-
|
86 |
slides_template: str,
|
87 |
output_file_path: pathlib.Path
|
88 |
) -> List:
|
89 |
"""
|
90 |
Create and save a PowerPoint presentation file containing the content in JSON format.
|
91 |
|
92 |
-
:param
|
93 |
:param slides_template: The PPTX template to use.
|
94 |
:param output_file_path: The path of the PPTX file to save as.
|
95 |
:return: A list of presentation title and slides headers.
|
96 |
"""
|
97 |
|
98 |
-
# The structured "JSON" might contain trailing commas, so using json5
|
99 |
-
parsed_data = json5.loads(structured_data)
|
100 |
presentation = pptx.Presentation(GlobalConfig.PPTX_TEMPLATE_FILES[slides_template]['file'])
|
101 |
slide_width_inch, slide_height_inch = _get_slide_width_height_inches(presentation)
|
102 |
|
@@ -237,21 +235,22 @@ def _handle_default_display(
|
|
237 |
|
238 |
status = False
|
239 |
|
240 |
-
if
|
241 |
-
if random.random() <
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
|
|
255 |
|
256 |
if status:
|
257 |
return
|
@@ -301,7 +300,8 @@ def _handle_display_image__in_foreground(
|
|
301 |
slide_height_inch: float
|
302 |
) -> bool:
|
303 |
"""
|
304 |
-
Create a slide with text and image using a picture placeholder layout.
|
|
|
305 |
|
306 |
:param presentation: The presentation object.
|
307 |
:param slide_json: The content of the slide as JSON data.
|
@@ -386,7 +386,8 @@ def _handle_display_image__in_background(
|
|
386 |
) -> bool:
|
387 |
"""
|
388 |
Add a slide with text and an image in the background. It works just like
|
389 |
-
`_handle_default_display()` but with a background image added.
|
|
|
390 |
|
391 |
:param presentation: The presentation object.
|
392 |
:param slide_json: The content of the slide as JSON data.
|
@@ -566,6 +567,14 @@ def _handle_icons_ideas(
|
|
566 |
for run in paragraph.runs:
|
567 |
run.font.color.theme_color = pptx.enum.dml.MSO_THEME_COLOR.TEXT_2
|
568 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
569 |
return True
|
570 |
|
571 |
return False
|
@@ -928,7 +937,6 @@ if __name__ == '__main__':
|
|
928 |
"AI for predicting student performance and dropout rates"
|
929 |
],
|
930 |
"key_message": "AI is personalizing education and improving student outcomes",
|
931 |
-
"img_keywords": "education, personalized learning, intelligent tutoring, student performance"
|
932 |
},
|
933 |
{
|
934 |
"heading": "Step-by-Step: AI Development Process",
|
@@ -940,7 +948,7 @@ if __name__ == '__main__':
|
|
940 |
">> Deploy and monitor the AI system"
|
941 |
],
|
942 |
"key_message": "Developing AI involves a structured process from problem definition to deployment",
|
943 |
-
"img_keywords": "
|
944 |
},
|
945 |
{
|
946 |
"heading": "AI Icons: Key Aspects",
|
@@ -974,7 +982,7 @@ if __name__ == '__main__':
|
|
974 |
generate_powerpoint_presentation(
|
975 |
json5.loads(_JSON_DATA),
|
976 |
output_file_path=path,
|
977 |
-
slides_template='
|
978 |
)
|
979 |
print(f'File path: {path}')
|
980 |
|
|
|
82 |
|
83 |
|
84 |
def generate_powerpoint_presentation(
|
85 |
+
parsed_data: dict,
|
86 |
slides_template: str,
|
87 |
output_file_path: pathlib.Path
|
88 |
) -> List:
|
89 |
"""
|
90 |
Create and save a PowerPoint presentation file containing the content in JSON format.
|
91 |
|
92 |
+
:param parsed_data: The presentation content as parsed JSON data.
|
93 |
:param slides_template: The PPTX template to use.
|
94 |
:param output_file_path: The path of the PPTX file to save as.
|
95 |
:return: A list of presentation title and slides headers.
|
96 |
"""
|
97 |
|
|
|
|
|
98 |
presentation = pptx.Presentation(GlobalConfig.PPTX_TEMPLATE_FILES[slides_template]['file'])
|
99 |
slide_width_inch, slide_height_inch = _get_slide_width_height_inches(presentation)
|
100 |
|
|
|
235 |
|
236 |
status = False
|
237 |
|
238 |
+
if 'img_keywords' in slide_json:
|
239 |
+
if random.random() < IMAGE_DISPLAY_PROBABILITY:
|
240 |
+
if random.random() < FOREGROUND_IMAGE_PROBABILITY:
|
241 |
+
status = _handle_display_image__in_foreground(
|
242 |
+
presentation,
|
243 |
+
slide_json,
|
244 |
+
slide_width_inch,
|
245 |
+
slide_height_inch
|
246 |
+
)
|
247 |
+
else:
|
248 |
+
status = _handle_display_image__in_background(
|
249 |
+
presentation,
|
250 |
+
slide_json,
|
251 |
+
slide_width_inch,
|
252 |
+
slide_height_inch
|
253 |
+
)
|
254 |
|
255 |
if status:
|
256 |
return
|
|
|
300 |
slide_height_inch: float
|
301 |
) -> bool:
|
302 |
"""
|
303 |
+
Create a slide with text and image using a picture placeholder layout. If not image keyword is
|
304 |
+
available, it will add only text to the slide.
|
305 |
|
306 |
:param presentation: The presentation object.
|
307 |
:param slide_json: The content of the slide as JSON data.
|
|
|
386 |
) -> bool:
|
387 |
"""
|
388 |
Add a slide with text and an image in the background. It works just like
|
389 |
+
`_handle_default_display()` but with a background image added. If not image keyword is
|
390 |
+
available, it will add only text to the slide.
|
391 |
|
392 |
:param presentation: The presentation object.
|
393 |
:param slide_json: The content of the slide as JSON data.
|
|
|
567 |
for run in paragraph.runs:
|
568 |
run.font.color.theme_color = pptx.enum.dml.MSO_THEME_COLOR.TEXT_2
|
569 |
|
570 |
+
_add_text_at_bottom(
|
571 |
+
slide=slide,
|
572 |
+
slide_width_inch=slide_width_inch,
|
573 |
+
slide_height_inch=slide_height_inch,
|
574 |
+
text='More icons available in the SlideDeck AI repository',
|
575 |
+
hyperlink='https://github.com/barun-saha/slide-deck-ai/tree/main/icons/png128'
|
576 |
+
)
|
577 |
+
|
578 |
return True
|
579 |
|
580 |
return False
|
|
|
937 |
"AI for predicting student performance and dropout rates"
|
938 |
],
|
939 |
"key_message": "AI is personalizing education and improving student outcomes",
|
|
|
940 |
},
|
941 |
{
|
942 |
"heading": "Step-by-Step: AI Development Process",
|
|
|
948 |
">> Deploy and monitor the AI system"
|
949 |
],
|
950 |
"key_message": "Developing AI involves a structured process from problem definition to deployment",
|
951 |
+
"img_keywords": ""
|
952 |
},
|
953 |
{
|
954 |
"heading": "AI Icons: Key Aspects",
|
|
|
982 |
generate_powerpoint_presentation(
|
983 |
json5.loads(_JSON_DATA),
|
984 |
output_file_path=path,
|
985 |
+
slides_template='Basic'
|
986 |
)
|
987 |
print(f'File path: {path}')
|
988 |
|