Spaces:
Running
Running
File size: 12,789 Bytes
83d53eb 7d4095f 83d53eb 7d4095f 83d53eb a0a42c1 83d53eb 3f3a8ea 83d53eb a0a42c1 83d53eb a0a42c1 83d53eb 0ecf42c 83d53eb a0a42c1 83d53eb a0a42c1 83d53eb a0a42c1 cc32d02 a0a42c1 48fce0c cc32d02 a0a42c1 cc32d02 a0a42c1 cc32d02 83d53eb cc32d02 83d53eb cc32d02 83d53eb cc32d02 83d53eb cc32d02 83d53eb cc32d02 83d53eb cc32d02 83d53eb 41fc817 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
import os
from dotenv import load_dotenv
import google.generativeai as genai
import pdfplumber
import re
class PresentationAssistant:
def __init__(self, api_key=None, template_path="./template.md"):
"""Initialize the assistant with an API key."""
# Get API key from parameter
self.gemini_api_key = api_key
if not self.gemini_api_key:
raise ValueError("Missing Gemini API key. Please provide your API key.")
# Initialize Gemini client
genai.configure(api_key=self.gemini_api_key)
self.model = genai.GenerativeModel('gemini-1.5-pro')
self.template_path = template_path
def read_document(self, file_path):
"""Read content from a document file."""
try:
if file_path.lower().endswith('.pdf'):
return self.read_pdf(file_path)
with open(file_path, 'r', encoding='utf-8') as file:
return file.read()
except Exception as e:
print(f"Error reading file {file_path}: {str(e)}")
return ""
def read_pdf(self, file_path):
"""Extract text from PDF file."""
try:
text = ""
with pdfplumber.open(file_path) as pdf:
for page in pdf.pages:
text += page.extract_text() + "\n"
print(f"Successfully extracted {len(text)} characters from PDF")
return text
except Exception as e:
print(f"Error extracting text from PDF {file_path}: {str(e)}")
return ""
def analyze_documents(self, directory_path, query):
"""Analyze documents in the directory based on the query."""
relevant_content = []
try:
if os.path.isfile(directory_path):
relevant_content.extend(self.process_single_file(directory_path, query))
else:
for root, _, files in os.walk(directory_path):
for file in files:
if file.endswith(('.txt', '.md', '.pdf', '.doc', '.docx')):
file_path = os.path.join(root, file)
relevant_content.extend(self.process_single_file(file_path, query))
print(f"Found relevant content: {len(relevant_content)} sections")
return relevant_content
except Exception as e:
print(f"Error analyzing documents: {str(e)}")
return []
def process_single_file(self, file_path, query):
"""Process a single file to extract relevant information."""
relevant_content = []
content = self.read_document(file_path)
if content:
prompt = f"""
Based on this query: {query}
Extract relevant information from this content: {content}
Focus on key points and important details that would be suitable for a presentation.
Return only the relevant information in a concise format.
"""
try:
response = self.model.generate_content(prompt)
if response.text.strip():
relevant_content.append(response.text)
print(f"Successfully processed file: {file_path}")
except Exception as e:
print(f"Error processing file {file_path}: {str(e)}")
return relevant_content
def generate_presentation_content(self, relevant_content, num_slides):
"""Generate presentation content including title, subtitle, and bullet points."""
if not relevant_content:
raise ValueError("No content provided for presentation generation")
prompt = f"""
Create a presentation with {num_slides} slides based on this content: {relevant_content}
Important: For each bullet point, start with a relevant emoji that matches the content of that point.
Choose emojis that enhance understanding and engagement.
Follow this format exactly:
Title: [Emoji] [Overall presentation title]
(Example - Title: π Innovation in Education)
Slide 1: [Slide title]
- π― [Key point 1 with relevant emoji]
- π‘ [Key point 2 with relevant emoji]
- π [Key point 3 with relevant emoji]
Slide 2: [Slide title]
- π± [Key point 1 with relevant emoji]
- π [Key point 2 with relevant emoji]
- π€ [Key point 3 with relevant emoji]
[Continue for remaining slides...]
Make sure to proof-read all text for possible errors such as missing hyphens.
(Example: 'solutionoriented' instead of 'solution-oriented')
Make each slide focused and concise. Use engaging titles and ensure each emoji meaningfully relates to its point.
For example:
- Use π for learning/education points
- Use π― for goals/objectives
- Use π‘ for ideas/insights
- Use π€ for collaboration/partnership
- Use π± for growth/development
- Use π for progress/advancement
"""
try:
response = self.model.generate_content(prompt)
print("Generated presentation content successfully")
return response.text
except Exception as e:
print(f"Error generating presentation content: {str(e)}")
return None
def parse_presentation_content(self, content):
"""Parse generated presentation content into slides."""
lines = content.split('\n')
title, subtitle = "", ""
slides = []
current_slide = None
for line in lines:
line = line.strip()
if not line:
continue
if line.startswith("Title:"):
title = line.replace("Title:", "").strip()
elif line.startswith("Subtitle:"):
subtitle = line.replace("Subtitle:", "").strip()
elif line.startswith("Slide"):
if current_slide:
slides.append(current_slide)
slide_title = line.split(":", 1)[1].strip() if ":" in line else line
current_slide = {"title": slide_title, "points": []}
elif line.startswith("-"):
if current_slide:
point_text = line.replace("-", "").strip()
# Keep emojis but remove other markdown characters
point_text = ''.join(c for c in point_text if not c in ['*', '_', '`', '#'])
current_slide["points"].append(point_text)
if current_slide:
slides.append(current_slide)
print(f"Parsed content - Title: {title}, Subtitle: {subtitle}, Slides: {len(slides)}")
return title, subtitle, slides
def write_presentation_file(self, title, subtitle, slides):
"""Write the presentation to example.md file."""
try:
# Using Slidev's mint theme with custom settings
header_section = """---
theme: mint
title: Slide Duck Presentation
titleTemplate: '%s'
fonts:
sans: 'Poppins'
weights: '400,500,700'
css: |
.slidev-layout {
background-color: white;
}
.pill {
@apply bg-green-50 text-gray-600 px-4 py-2 rounded-full inline-block mb-2 border border-green-200;
}
.text-bar {
@apply bg-green-50 text-gray-600 px-6 py-3 rounded-full my-4 border border-green-200;
}
.emoji-point {
@apply flex items-center gap-2;
}
.emoji {
@apply text-xl;
}
layout: center
---"""
# Title slide template
title_slide = f"""
# {title}
<div class="text-bar">
{subtitle}
</div>
---"""
# Initialize presentation with header and title slide
presentation_content = [header_section, title_slide]
# Add content slides
for slide in slides:
slide_content = f"""
# {slide['title']}
<div class="grid grid-cols-1 gap-4">
"""
for point in slide['points']:
slide_content += f' <div class="pill">{point}</div>\n'
slide_content += """
</div>
---"""
presentation_content.append(slide_content)
# Write to file
with open('example.md', 'w', encoding='utf-8') as f:
f.write('\n'.join(presentation_content))
print(f"Successfully wrote presentation with {len(slides)} slides to example.md")
except Exception as e:
print(f"Error writing presentation file: {str(e)}")
raise
def create_presentation(self, directory_path, query, num_slides):
"""Create the full presentation."""
try:
# Analyze documents and extract relevant content
relevant_content = self.analyze_documents(directory_path, query)
if not relevant_content:
raise ValueError("No relevant content found in documents")
# Generate presentation content
presentation_content = self.generate_presentation_content(relevant_content, num_slides)
if not presentation_content:
raise ValueError("Failed to generate presentation content")
# Parse the generated content
title, subtitle, slides = self.parse_presentation_content(presentation_content)
# Write the presentation file
self.write_presentation_file(title, subtitle, slides)
return True
except Exception as e:
print(f"Error creating presentation: {str(e)}")
raise
def export_to_pptx(self, title, subtitle, slides, output_path="presentation.pptx"):
"""Export the presentation to PowerPoint format."""
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.enum.text import PP_ALIGN
from pptx.dml.color import RGBColor
# Create presentation
prs = Presentation()
# Set slide dimensions to 16:9
prs.slide_width = Inches(16)
prs.slide_height = Inches(9)
# Design constants
TITLE_FONT_SIZE = Pt(44)
SUBTITLE_FONT_SIZE = Pt(32)
BODY_FONT_SIZE = Pt(24)
ISP_BLUE = RGBColor(0, 91, 172) # #005BAC
ISP_LIGHT_BLUE = RGBColor(176, 224, 230) # #B0E0E6
# Title slide
title_slide_layout = prs.slide_layouts[0] # Title slide layout
slide = prs.slides.add_slide(title_slide_layout)
# Set title
title_shape = slide.shapes.title
title_shape.text = title
title_shape.text_frame.paragraphs[0].font.size = TITLE_FONT_SIZE
title_shape.text_frame.paragraphs[0].font.color.rgb = ISP_BLUE
# Set subtitle
subtitle_shape = slide.placeholders[1]
subtitle_shape.text = subtitle
subtitle_shape.text_frame.paragraphs[0].font.size = SUBTITLE_FONT_SIZE
# Content slides
for slide_content in slides:
content_slide_layout = prs.slide_layouts[1] # Content slide layout
slide = prs.slides.add_slide(content_slide_layout)
# Set slide title
title_shape = slide.shapes.title
title_shape.text = slide_content['title']
title_shape.text_frame.paragraphs[0].font.size = TITLE_FONT_SIZE
title_shape.text_frame.paragraphs[0].font.color.rgb = ISP_BLUE
# Add points
body_shape = slide.placeholders[1]
text_frame = body_shape.text_frame
text_frame.clear() # Clear default text
for point in slide_content['points']:
p = text_frame.add_paragraph()
p.text = point
p.font.size = BODY_FONT_SIZE
p.level = 0 # Top level bullet point
# Style the bullet points
p.font.color.rgb = RGBColor(85, 85, 85) # Dark gray for better readability
# Add shape for visual interest
pill_left = Inches(1)
pill_top = Inches(6)
pill_width = Inches(14)
pill_height = Inches(0.5)
pill = slide.shapes.add_shape(
1, pill_left, pill_top, pill_width, pill_height
)
pill.fill.solid()
pill.fill.fore_color.rgb = ISP_LIGHT_BLUE
pill.line.color.rgb = ISP_BLUE
# Save presentation
prs.save(output_path)
return output_path |