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