ans123 commited on
Commit
8cf2a7f
·
verified ·
1 Parent(s): 1c896d6

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +1167 -0
app.py ADDED
@@ -0,0 +1,1167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import time
4
+ import re
5
+ import math
6
+ import tempfile
7
+ import uuid
8
+ from io import BytesIO
9
+ import gradio as gr
10
+ import torch
11
+ from transformers import AutoTokenizer, AutoModelForCausalLM
12
+ from pptx import Presentation
13
+ from pptx.util import Pt, Inches
14
+ from pptx.enum.text import PP_ALIGN
15
+ from pptx.dml.color import RGBColor
16
+ from pptx.chart.data import CategoryChartData, ChartData
17
+ from pptx.enum.chart import XL_CHART_TYPE, XL_LEGEND_POSITION
18
+
19
+ # Set device
20
+ device = "cuda" if torch.cuda.is_available() else "cpu"
21
+ print(f"Using device: {device}")
22
+
23
+ # Model configuration
24
+ MODEL_NAME = "HuggingFaceH4/zephyr-7b-beta"
25
+ MAX_LENGTH = 4000
26
+ TEMPERATURE = 0.7
27
+
28
+ # ===== ENHANCED PROPOSAL GENERATION FUNCTIONS ===== #
29
+
30
+ def generate_proposal(model, tokenizer, description, temperature=0.7, max_length=4000):
31
+ """Generate a detailed, well-structured proposal from a description"""
32
+ system_prompt = """You are an expert proposal writer with years of experience crafting successful business and project proposals.
33
+ Your task is to create a comprehensive, professional proposal that is detailed, specific, and tailored to the project description.
34
+ Follow a structured format with clear sections, use professional language, and include concrete details that make the proposal compelling.
35
+ Be specific with numbers, timelines, and methodologies where appropriate."""
36
+
37
+ user_prompt = f"""Create a detailed project proposal for the following project description:
38
+
39
+ "{description}"
40
+
41
+ Your proposal must include these sections:
42
+ 1. Executive Summary - Concise overview of the entire proposal (1-2 paragraphs)
43
+ 2. Project Background - Context, history, and need for the project (2-3 paragraphs)
44
+ 3. Goals and Objectives - Clear, measurable outcomes using SMART criteria (specific, measurable, achievable, relevant, time-bound)
45
+ 4. Methodology and Approach - Detailed explanation of how the project will be executed, including specific techniques and frameworks
46
+ 5. Timeline - Realistic schedule with key milestones and deliverables (presented in chronological order with approximate dates)
47
+ 6. Budget Considerations - Cost breakdown by category with justifications
48
+ 7. Expected Outcomes - Tangible and intangible benefits with metrics for measuring success
49
+ 8. Team and Resources - Key personnel, their qualifications, and resource requirements
50
+ 9. Risk Assessment - Potential challenges and mitigation strategies
51
+ 10. Conclusion - Compelling closing with clear next steps
52
+
53
+ For each section, include specific details that would convince stakeholders to approve this project.
54
+ Use professional language and maintain a confident, authoritative tone throughout.
55
+ Format each section with clear headings and organized paragraphs."""
56
+
57
+ # Format the prompt according to the model's expected format
58
+ # Zephyr uses a specific format with system and user messages
59
+ messages = [
60
+ {"role": "system", "content": system_prompt},
61
+ {"role": "user", "content": user_prompt}
62
+ ]
63
+
64
+ # Convert messages to the format expected by the model
65
+ prompt = tokenizer.apply_chat_template(messages, tokenize=False)
66
+
67
+ # Tokenize and generate
68
+ inputs = tokenizer(prompt, return_tensors="pt").to(device)
69
+
70
+ with torch.no_grad():
71
+ outputs = model.generate(
72
+ inputs.input_ids,
73
+ max_new_tokens=max_length,
74
+ temperature=temperature,
75
+ do_sample=True,
76
+ top_p=0.9, # Add nucleus sampling for better quality
77
+ top_k=50, # Limit vocab to top 50 tokens at each step
78
+ repetition_penalty=1.2 # Reduce repetition
79
+ )
80
+
81
+ full_output = tokenizer.decode(outputs[0], skip_special_tokens=True)
82
+
83
+ # Extract just the generated proposal without the prompt
84
+ proposal = full_output[len(prompt):].strip()
85
+
86
+ # Post-processing to ensure clean formatting
87
+ proposal = clean_proposal_format(proposal)
88
+
89
+ return proposal
90
+
91
+ def clean_proposal_format(proposal_text):
92
+ """Clean up the proposal text for better formatting"""
93
+
94
+ # Remove any assistant prefixes that might be generated
95
+ proposal_text = re.sub(r'^(Assistant:|A:|Response:)\s*', '', proposal_text)
96
+
97
+ # Ensure section titles are properly formatted
98
+ section_titles = [
99
+ "Executive Summary", "Project Background", "Goals and Objectives",
100
+ "Methodology and Approach", "Timeline", "Budget Considerations",
101
+ "Expected Outcomes", "Team and Resources", "Risk Assessment", "Conclusion"
102
+ ]
103
+
104
+ for title in section_titles:
105
+ # Replace various formats of section titles with consistent formatting
106
+ proposal_text = re.sub(
107
+ rf'(?i)(^|\n)[\d\.\s]*{title}[\s\:]*(\n|\r)',
108
+ f'\n\n{title}\n\n',
109
+ proposal_text
110
+ )
111
+
112
+ # Ensure consistent paragraph breaks
113
+ proposal_text = re.sub(r'\n{3,}', '\n\n', proposal_text)
114
+
115
+ # Ensure bullet points are consistent
116
+ proposal_text = re.sub(r'\n\s*•\s*', '\n- ', proposal_text)
117
+ proposal_text = re.sub(r'\n\s*\*\s*', '\n- ', proposal_text)
118
+
119
+ return proposal_text
120
+
121
+ # ===== ENHANCED POWERPOINT GENERATION FUNCTIONS ===== #
122
+
123
+ def create_slides(proposal, project_title="Project Proposal"):
124
+ """Create professional PowerPoint slides from the proposal"""
125
+
126
+ # Create presentation with widescreen dimensions
127
+ prs = Presentation()
128
+ prs.slide_width = Inches(13.33)
129
+ prs.slide_height = Inches(7.5)
130
+
131
+ # Define a professional color scheme
132
+ colors = {
133
+ 'primary': RGBColor(0, 112, 192), # Blue
134
+ 'secondary': RGBColor(0, 176, 80), # Green
135
+ 'accent1': RGBColor(255, 102, 0), # Orange
136
+ 'accent2': RGBColor(112, 48, 160), # Purple
137
+ 'dark': RGBColor(54, 54, 54), # Dark Gray
138
+ 'light': RGBColor(244, 244, 244) # Light Gray
139
+ }
140
+
141
+ # Add title slide with professional styling
142
+ title_slide = prs.slides.add_slide(prs.slide_layouts[0])
143
+ title_slide.shapes.title.text = project_title
144
+ title_slide.shapes.title.text_frame.paragraphs[0].font.size = Pt(44)
145
+ title_slide.shapes.title.text_frame.paragraphs[0].font.color.rgb = colors['primary']
146
+ title_slide.shapes.title.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER
147
+
148
+ # Add subtitle
149
+ subtitle = title_slide.placeholders[1]
150
+ subtitle.text = "Professional Project Proposal"
151
+ subtitle.text_frame.paragraphs[0].font.size = Pt(28)
152
+ subtitle.text_frame.paragraphs[0].font.color.rgb = colors['dark']
153
+
154
+ # Add background shape for visual interest
155
+ left = top = 0
156
+ width = prs.slide_width
157
+ height = prs.slide_height
158
+
159
+ # Add decorative element to title slide
160
+ shape = title_slide.shapes.add_shape(
161
+ 1, Inches(0), Inches(6.5), prs.slide_width, Inches(1)
162
+ )
163
+ shape.fill.solid()
164
+ shape.fill.fore_color.rgb = colors['primary']
165
+ shape.line.color.rgb = colors['primary']
166
+
167
+ # List of sections to look for
168
+ sections = [
169
+ "Executive Summary",
170
+ "Project Background",
171
+ "Goals and Objectives",
172
+ "Methodology and Approach",
173
+ "Timeline",
174
+ "Budget Considerations",
175
+ "Expected Outcomes",
176
+ "Team and Resources",
177
+ "Risk Assessment",
178
+ "Conclusion"
179
+ ]
180
+
181
+ # Create a slide for table of contents
182
+ toc_slide = prs.slides.add_slide(prs.slide_layouts[2])
183
+ toc_slide.shapes.title.text = "Table of Contents"
184
+ toc_slide.shapes.title.text_frame.paragraphs[0].font.size = Pt(40)
185
+ toc_slide.shapes.title.text_frame.paragraphs[0].font.color.rgb = colors['primary']
186
+
187
+ # Add decorative element to TOC slide
188
+ shape = toc_slide.shapes.add_shape(
189
+ 1, Inches(0), Inches(0), Inches(1), prs.slide_height
190
+ )
191
+ shape.fill.solid()
192
+ shape.fill.fore_color.rgb = colors['primary']
193
+ shape.line.color.rgb = colors['primary']
194
+
195
+ # Add TOC content
196
+ toc_content = toc_slide.placeholders[1].text_frame
197
+ for i, section in enumerate(sections, 1):
198
+ p = toc_content.add_paragraph()
199
+ p.text = f"{i}. {section}"
200
+ p.font.size = Pt(24)
201
+ p.font.color.rgb = colors['dark']
202
+ p.space_after = Pt(12)
203
+
204
+ # Split text into paragraphs and identify sections
205
+ paragraphs = proposal.split('\n\n')
206
+
207
+ # Process each paragraph
208
+ current_section = None
209
+ current_content = []
210
+ found_sections = []
211
+
212
+ for para in paragraphs:
213
+ para = para.strip()
214
+ if not para:
215
+ continue
216
+
217
+ # Check if this is a section header
218
+ is_header = False
219
+ for section in sections:
220
+ # More robust section detection
221
+ if (section.lower() in para.lower() and len(para) < 100) or \
222
+ re.match(r'^[0-9]+\.?\s*' + section, para, re.IGNORECASE):
223
+ # Save previous section
224
+ if current_section and current_content:
225
+ found_sections.append((current_section, current_content))
226
+
227
+ # Start new section
228
+ current_section = section # Use standard section name
229
+ current_content = []
230
+ is_header = True
231
+ break
232
+
233
+ if not is_header:
234
+ current_content.append(para)
235
+
236
+ # Add the last section
237
+ if current_section and current_content:
238
+ found_sections.append((current_section, current_content))
239
+
240
+ # Create slides for each section
241
+ for section_index, (title, content_paras) in enumerate(found_sections):
242
+ # Section title slide with visual distinction
243
+ section_slide = prs.slides.add_slide(prs.slide_layouts[2])
244
+ section_slide.shapes.title.text = title
245
+ section_slide.shapes.title.text_frame.paragraphs[0].font.size = Pt(40)
246
+ section_slide.shapes.title.text_frame.paragraphs[0].font.color.rgb = colors['primary']
247
+
248
+ # Add decorative accent based on section type
249
+ accent_color = colors['primary']
250
+ if "goal" in title.lower() or "objective" in title.lower():
251
+ accent_color = colors['secondary']
252
+ elif "risk" in title.lower():
253
+ accent_color = colors['accent1']
254
+ elif "conclusion" in title.lower():
255
+ accent_color = colors['accent2']
256
+
257
+ # Add section number
258
+ subtitle = section_slide.placeholders[1]
259
+ subtitle.text = f"Section {section_index + 1}"
260
+ subtitle.text_frame.paragraphs[0].font.size = Pt(28)
261
+ subtitle.text_frame.paragraphs[0].font.color.rgb = accent_color
262
+
263
+ # Decorative shape for section divider
264
+ shape = section_slide.shapes.add_shape(
265
+ 1, Inches(0), Inches(6.5), prs.slide_width, Inches(1)
266
+ )
267
+ shape.fill.solid()
268
+ shape.fill.fore_color.rgb = accent_color
269
+ shape.line.color.rgb = accent_color
270
+
271
+ # Content slides with better formatting
272
+ current_slide = None
273
+ text_frame = None
274
+ paragraphs_on_slide = 0
275
+
276
+ for para_index, para in enumerate(content_paras):
277
+ # Detect if paragraph is a bullet point
278
+ is_bullet = para.strip().startswith("-") or para.strip().startswith("*")
279
+ is_numbered = bool(re.match(r'^\d+\.', para.strip()))
280
+
281
+ # Start a new slide if needed
282
+ # Fewer paragraphs per slide for better readability
283
+ max_paragraphs = 3 if len(para) > 200 else 4
284
+ if current_slide is None or paragraphs_on_slide >= max_paragraphs:
285
+ current_slide = prs.slides.add_slide(prs.slide_layouts[1])
286
+ current_slide.shapes.title.text = title
287
+ current_slide.shapes.title.text_frame.paragraphs[0].font.size = Pt(36)
288
+ current_slide.shapes.title.text_frame.paragraphs[0].font.color.rgb = colors['primary']
289
+
290
+ # Add decorative element
291
+ shape = current_slide.shapes.add_shape(
292
+ 1, Inches(0), Inches(0), Inches(0.3), prs.slide_height
293
+ )
294
+ shape.fill.solid()
295
+ shape.fill.fore_color.rgb = accent_color
296
+ shape.line.color.rgb = accent_color
297
+
298
+ text_frame = current_slide.placeholders[1].text_frame
299
+ text_frame.word_wrap = True
300
+ paragraphs_on_slide = 0
301
+
302
+ # Add subtitle for content continuation if not the first content slide
303
+ if para_index > 0:
304
+ p = text_frame.add_paragraph()
305
+ p.text = "Continued..."
306
+ p.font.italic = True
307
+ p.font.size = Pt(14)
308
+ p.font.color.rgb = colors['dark']
309
+ paragraphs_on_slide += 0.5 # Count as half a paragraph
310
+
311
+ # Add the paragraph with appropriate formatting
312
+ p = text_frame.add_paragraph()
313
+
314
+ # Clean up bullet points
315
+ if is_bullet:
316
+ clean_text = para.strip()[1:].strip()
317
+ p.text = clean_text
318
+ p.level = 1
319
+ elif is_numbered:
320
+ p.text = para.strip()
321
+ p.level = 1
322
+ else:
323
+ p.text = para.strip()
324
+
325
+ # Apply formatting
326
+ p.font.size = Pt(20)
327
+ p.font.color.rgb = colors['dark']
328
+ p.space_after = Pt(12)
329
+
330
+ # Highlight key phrases with color
331
+ text_frame.fit_text(max_size=Pt(20))
332
+
333
+ paragraphs_on_slide += 1
334
+
335
+ # Add a closing slide
336
+ closing_slide = prs.slides.add_slide(prs.slide_layouts[5])
337
+ title_shape = closing_slide.shapes.title
338
+ title_shape.text = "Thank You"
339
+ title_shape.text_frame.paragraphs[0].font.size = Pt(54)
340
+ title_shape.text_frame.paragraphs[0].font.color.rgb = colors['primary']
341
+ title_shape.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER
342
+
343
+ subtitle_shape = closing_slide.shapes.placeholders[1]
344
+ subtitle_shape.text = "Questions & Discussion"
345
+ subtitle_shape.text_frame.paragraphs[0].font.size = Pt(32)
346
+ subtitle_shape.text_frame.paragraphs[0].font.color.rgb = colors['dark']
347
+ subtitle_shape.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER
348
+
349
+ # Add large decorative shape to closing slide
350
+ shape = closing_slide.shapes.add_shape(
351
+ 1, Inches(0), Inches(6.5), prs.slide_width, Inches(1)
352
+ )
353
+ shape.fill.solid()
354
+ shape.fill.fore_color.rgb = colors['primary']
355
+ shape.line.color.rgb = colors['primary']
356
+
357
+ # Generate a unique filename
358
+ unique_id = str(uuid.uuid4())[:8]
359
+ output_path = f"proposal_slides_{unique_id}.pptx"
360
+
361
+ # Save the presentation
362
+ prs.save(output_path)
363
+ return output_path
364
+
365
+ # ===== CHARTS AND VISUALS FUNCTIONS ===== #
366
+
367
+ def add_charts_and_visuals(prs, proposal_text, include_visuals=True):
368
+ """Add professional charts and diagrams to the presentation based on proposal content"""
369
+ if not include_visuals:
370
+ return prs
371
+
372
+ # Extract potential data for charts from the proposal text
373
+ timeline_data = extract_timeline_data(proposal_text)
374
+ budget_data = extract_budget_data(proposal_text)
375
+ risk_data = extract_risk_data(proposal_text)
376
+
377
+ # Add a timeline slide if data available
378
+ if timeline_data and len(timeline_data) >= 2:
379
+ timeline_slide = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout
380
+
381
+ # Add title
382
+ title_shape = timeline_slide.shapes.add_textbox(
383
+ Inches(0.5), Inches(0.5), Inches(9), Inches(1)
384
+ )
385
+ title_frame = title_shape.text_frame
386
+ title_frame.text = "Project Timeline"
387
+ title_frame.paragraphs[0].font.size = Pt(40)
388
+ title_frame.paragraphs[0].font.bold = True
389
+ title_frame.paragraphs[0].font.color.rgb = RGBColor(0, 112, 192)
390
+
391
+ # Create chart data
392
+ chart_data = ChartData()
393
+ chart_data.categories = [item[0] for item in timeline_data] # Milestone names
394
+
395
+ # Convert timeline data to numeric values (months from start)
396
+ def month_to_number(month_name):
397
+ months = {'January': 1, 'February': 2, 'March': 3, 'April': 4,
398
+ 'May': 5, 'June': 6, 'July': 7, 'August': 8,
399
+ 'September': 9, 'October': 10, 'November': 11, 'December': 12}
400
+ for month, num in months.items():
401
+ if month.lower() in month_name.lower():
402
+ return num
403
+ return 0
404
+
405
+ # Normalize timeline values based on extracted data
406
+ start_month = min(month_to_number(item[1]) for item in timeline_data) if timeline_data else 1
407
+ timeline_values = [max(1, month_to_number(item[1]) - start_month + 1) for item in timeline_data]
408
+
409
+ chart_data.add_series('Duration (months)', timeline_values)
410
+
411
+ # Add chart to the slide
412
+ x, y, cx, cy = Inches(1), Inches(2), Inches(11), Inches(5)
413
+ chart = timeline_slide.shapes.add_chart(
414
+ XL_CHART_TYPE.BAR_CLUSTERED, x, y, cx, cy, chart_data
415
+ ).chart
416
+
417
+ # Style the chart
418
+ chart.has_legend = True
419
+ chart.legend.position = XL_LEGEND_POSITION.BOTTOM
420
+ chart.legend.include_in_layout = False
421
+
422
+ plot = chart.plots[0]
423
+ plot.has_data_labels = True
424
+ data_labels = plot.data_labels
425
+ data_labels.font.size = Pt(12)
426
+ data_labels.font.color.rgb = RGBColor(0, 0, 0)
427
+ data_labels.position = 2 # Inside End
428
+
429
+ # Add a budget breakdown slide if data available
430
+ if budget_data and len(budget_data) >= 2:
431
+ budget_slide = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout
432
+
433
+ # Add title
434
+ title_shape = budget_slide.shapes.add_textbox(
435
+ Inches(0.5), Inches(0.5), Inches(9), Inches(1)
436
+ )
437
+ title_frame = title_shape.text_frame
438
+ title_frame.text = "Budget Allocation"
439
+ title_frame.paragraphs[0].font.size = Pt(40)
440
+ title_frame.paragraphs[0].font.bold = True
441
+ title_frame.paragraphs[0].font.color.rgb = RGBColor(0, 112, 192)
442
+
443
+ # Create pie chart data
444
+ chart_data = CategoryChartData()
445
+ chart_data.categories = [item[0] for item in budget_data] # Category names
446
+ chart_data.add_series('Budget', [item[1] for item in budget_data]) # Values
447
+
448
+ # Add chart to the slide
449
+ x, y, cx, cy = Inches(2), Inches(2), Inches(9), Inches(5)
450
+ chart = budget_slide.shapes.add_chart(
451
+ XL_CHART_TYPE.PIE, x, y, cx, cy, chart_data
452
+ ).chart
453
+
454
+ # Style the chart
455
+ chart.has_legend = True
456
+ chart.legend.position = XL_LEGEND_POSITION.RIGHT
457
+ chart.legend.font.size = Pt(14)
458
+
459
+ plot = chart.plots[0]
460
+ plot.has_data_labels = True
461
+ data_labels = plot.data_labels
462
+ data_labels.font.size = Pt(12)
463
+ data_labels.position = 1 # Outside End
464
+ data_labels.number_format = '0%'
465
+
466
+ # Add a risk assessment matrix if data available
467
+ if risk_data and len(risk_data) >= 2:
468
+ risk_slide = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout
469
+
470
+ # Add title
471
+ title_shape = risk_slide.shapes.add_textbox(
472
+ Inches(0.5), Inches(0.5), Inches(9), Inches(1)
473
+ )
474
+ title_frame = title_shape.text_frame
475
+ title_frame.text = "Risk Assessment Matrix"
476
+ title_frame.paragraphs[0].font.size = Pt(40)
477
+ title_frame.paragraphs[0].font.bold = True
478
+ title_frame.paragraphs[0].font.color.rgb = RGBColor(0, 112, 192)
479
+
480
+ # Create a table for the risk matrix
481
+ rows, cols = len(risk_data) + 1, 3 # +1 for header
482
+ table_width, table_height = Inches(10), Inches(5)
483
+ table = risk_slide.shapes.add_table(
484
+ rows, cols,
485
+ Inches(1.5), Inches(2),
486
+ table_width, table_height
487
+ ).table
488
+
489
+ # Set column widths
490
+ table.columns[0].width = Inches(5) # Risk description
491
+ table.columns[1].width = Inches(2.5) # Probability
492
+ table.columns[2].width = Inches(2.5) # Impact
493
+
494
+ # Add header row
495
+ cell = table.cell(0, 0)
496
+ cell.text = "Risk Description"
497
+ cell.text_frame.paragraphs[0].font.bold = True
498
+ cell.text_frame.paragraphs[0].font.size = Pt(16)
499
+
500
+ cell = table.cell(0, 1)
501
+ cell.text = "Probability"
502
+ cell.text_frame.paragraphs[0].font.bold = True
503
+ cell.text_frame.paragraphs[0].font.size = Pt(16)
504
+
505
+ cell = table.cell(0, 2)
506
+ cell.text = "Impact"
507
+ cell.text_frame.paragraphs[0].font.bold = True
508
+ cell.text_frame.paragraphs[0].font.size = Pt(16)
509
+
510
+ # Add risk data rows
511
+ for i, (risk, probability, impact) in enumerate(risk_data, 1):
512
+ # Risk description
513
+ cell = table.cell(i, 0)
514
+ cell.text = risk
515
+ cell.text_frame.paragraphs[0].font.size = Pt(14)
516
+
517
+ # Probability
518
+ cell = table.cell(i, 1)
519
+ cell.text = probability
520
+ cell.text_frame.paragraphs[0].font.size = Pt(14)
521
+
522
+ # Impact
523
+ cell = table.cell(i, 2)
524
+ cell.text = impact
525
+ cell.text_frame.paragraphs[0].font.size = Pt(14)
526
+
527
+ # Color code based on overall risk level
528
+ prob_level = get_risk_level(probability)
529
+ impact_level = get_risk_level(impact)
530
+ overall_risk = prob_level * impact_level
531
+
532
+ # Set background color based on risk level
533
+ fill_color = RGBColor(255, 255, 255) # Default white
534
+ if overall_risk >= 9:
535
+ fill_color = RGBColor(255, 153, 153) # Red for high risk
536
+ elif overall_risk >= 4:
537
+ fill_color = RGBColor(255, 204, 153) # Orange for medium risk
538
+ else:
539
+ fill_color = RGBColor(198, 239, 206) # Green for low risk
540
+
541
+ for col in range(3):
542
+ table.cell(i, col).fill.solid()
543
+ table.cell(i, col).fill.fore_color.rgb = fill_color
544
+
545
+ # Add a SMART objectives slide - a generic visual we can add regardless of content
546
+ objectives_slide = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout
547
+
548
+ # Add title
549
+ title_shape = objectives_slide.shapes.add_textbox(
550
+ Inches(0.5), Inches(0.5), Inches(9), Inches(1)
551
+ )
552
+ title_frame = title_shape.text_frame
553
+ title_frame.text = "SMART Objectives Framework"
554
+ title_frame.paragraphs[0].font.size = Pt(40)
555
+ title_frame.paragraphs[0].font.bold = True
556
+ title_frame.paragraphs[0].font.color.rgb = RGBColor(0, 112, 192)
557
+
558
+ # Create a SmartArt-like diagram for SMART objectives
559
+ # Since python-pptx doesn't directly support SmartArt, we'll simulate it with shapes
560
+
561
+ # Center point for the circular layout
562
+ center_x, center_y = Inches(6.5), Inches(4)
563
+ radius = Inches(2.5)
564
+
565
+ # SMART components with colors
566
+ smart_components = [
567
+ ("Specific", RGBColor(91, 155, 213)), # Blue
568
+ ("Measurable", RGBColor(112, 173, 71)), # Green
569
+ ("Achievable", RGBColor(237, 125, 49)), # Orange
570
+ ("Relevant", RGBColor(165, 105, 189)), # Purple
571
+ ("Time-bound", RGBColor(68, 114, 196)) # Dark Blue
572
+ ]
573
+
574
+ # Add central circle
575
+ central_shape = objectives_slide.shapes.add_shape(
576
+ 1, center_x - Inches(1), center_y - Inches(1), Inches(2), Inches(2)
577
+ )
578
+ central_shape.fill.solid()
579
+ central_shape.fill.fore_color.rgb = RGBColor(0, 112, 192)
580
+ central_shape.line.color.rgb = RGBColor(255, 255, 255)
581
+ central_shape.line.width = Pt(2)
582
+
583
+ # Add text to central circle
584
+ text_frame = central_shape.text_frame
585
+ text_frame.text = "SMART\nObjectives"
586
+ text_frame.paragraphs[0].alignment = 1 # Center
587
+ text_frame.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255)
588
+ text_frame.paragraphs[0].font.size = Pt(18)
589
+ text_frame.paragraphs[0].font.bold = True
590
+
591
+ # Add surrounding circles for each SMART component
592
+ for i, (component, color) in enumerate(smart_components):
593
+ angle = (2 * 3.14159 * i) / len(smart_components)
594
+ x = center_x + radius * 0.8 * math.cos(angle) - Inches(1)
595
+ y = center_y + radius * 0.8 * math.sin(angle) - Inches(0.75)
596
+
597
+ # Component circle
598
+ component_shape = objectives_slide.shapes.add_shape(
599
+ 1, x, y, Inches(2), Inches(1.5)
600
+ )
601
+ component_shape.fill.solid()
602
+ component_shape.fill.fore_color.rgb = color
603
+ component_shape.line.color.rgb = RGBColor(255, 255, 255)
604
+ component_shape.line.width = Pt(2)
605
+
606
+ # Add component text
607
+ text_frame = component_shape.text_frame
608
+ text_frame.text = component
609
+ text_frame.paragraphs[0].alignment = 1 # Center
610
+ text_frame.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255)
611
+ text_frame.paragraphs[0].font.size = Pt(16)
612
+ text_frame.paragraphs[0].font.bold = True
613
+
614
+ # Add connecting line
615
+ line = objectives_slide.shapes.add_connector(
616
+ 3, # Straight connector
617
+ central_shape.left + Inches(1),
618
+ central_shape.top + Inches(1),
619
+ component_shape.left + Inches(1),
620
+ component_shape.top + Inches(0.75)
621
+ )
622
+ line.line.color.rgb = RGBColor(0, 0, 0)
623
+ line.line.width = Pt(1.5)
624
+
625
+ return prs
626
+
627
+ def extract_timeline_data(proposal_text):
628
+ """Extract timeline data from the proposal text using regex patterns"""
629
+
630
+ # Look for timeline section
631
+ timeline_section = re.search(r'(?i)Timeline.*?(?=\n\n[A-Z]|$)', proposal_text, re.DOTALL)
632
+ if not timeline_section:
633
+ # Generate example data if no real data found
634
+ return [
635
+ ("Project Initiation", "Month 1"),
636
+ ("Requirements Analysis", "Month 2"),
637
+ ("Design Phase", "Month 3"),
638
+ ("Implementation", "Month 4-5"),
639
+ ("Testing", "Month 6"),
640
+ ("Deployment", "Month 7"),
641
+ ("Post-Implementation Review", "Month 8")
642
+ ]
643
+
644
+ timeline_text = timeline_section.group(0)
645
+
646
+ # Look for milestone patterns like "Phase 1: Project Initiation (Month 1)"
647
+ # or "Project Initiation - Month 1" or "Week 1-2: Requirements Gathering"
648
+ milestone_patterns = [
649
+ r'([^:]+):\s*([^(]+)\s*\(([^)]+)\)', # Phase 1: Project Initiation (Month 1)
650
+ r'([^-]+)\s*-\s*([^(]+)', # Project Initiation - Month 1
651
+ r'((?:Week|Month)[^:]+):\s*([^(]+)', # Week 1-2: Requirements Gathering
652
+ r'([^:]+):\s*([^(]+)' # Any other pattern with colon
653
+ ]
654
+
655
+ extracted_data = []
656
+ for pattern in milestone_patterns:
657
+ matches = re.finditer(pattern, timeline_text)
658
+ for match in matches:
659
+ if len(match.groups()) >= 2:
660
+ milestone = match.group(1).strip()
661
+ timeframe = match.group(2).strip()
662
+
663
+ # Clean up the milestone and timeframe
664
+ milestone = re.sub(r'^[0-9]+\.\s*', '', milestone) # Remove leading numbers
665
+
666
+ # Add to extracted data
667
+ extracted_data.append((milestone, timeframe))
668
+
669
+ # If no data extracted, create example data
670
+ if not extracted_data:
671
+ # Generate some example data
672
+ return [
673
+ ("Project Initiation", "Month 1"),
674
+ ("Requirements Analysis", "Month 2"),
675
+ ("Design Phase", "Month 3"),
676
+ ("Implementation", "Month 4-5"),
677
+ ("Testing", "Month 6"),
678
+ ("Deployment", "Month 7"),
679
+ ("Post-Implementation Review", "Month 8")
680
+ ]
681
+
682
+ return extracted_data
683
+
684
+ def extract_budget_data(proposal_text):
685
+ """Extract budget data from the proposal text using regex patterns"""
686
+
687
+ # Look for budget section
688
+ budget_section = re.search(r'(?i)Budget.*?(?=\n\n[A-Z]|$)', proposal_text, re.DOTALL)
689
+ if not budget_section:
690
+ # Generate example data if no real data found
691
+ return [
692
+ ("Hardware", 30),
693
+ ("Software", 25),
694
+ ("Personnel", 35),
695
+ ("Training", 10),
696
+ ("Contingency", 10)
697
+ ]
698
+
699
+ budget_text = budget_section.group(0)
700
+
701
+ # Pattern for budget items with percentages or amounts
702
+ # e.g. "Hardware: $50,000 (20%)" or "Personnel: 35% of total budget"
703
+ budget_patterns = [
704
+ r'([^:]+):\s*\$?[\d,]+\s*\((\d+)%\)', # Hardware: $50,000 (20%)
705
+ r'([^:]+):\s*(\d+)%', # Personnel: 35%
706
+ r'([^-]+)\s*-\s*(\d+)%' # Software - 25%
707
+ ]
708
+
709
+ extracted_data = []
710
+ for pattern in budget_patterns:
711
+ matches = re.finditer(pattern, budget_text)
712
+ for match in matches:
713
+ if len(match.groups()) >= 2:
714
+ category = match.group(1).strip()
715
+ percentage = int(match.group(2).strip())
716
+
717
+ # Clean up the category
718
+ category = re.sub(r'^[0-9]+\.\s*', '', category) # Remove leading numbers
719
+
720
+ # Add to extracted data
721
+ extracted_data.append((category, percentage))
722
+
723
+ # If no data extracted, look for dollar amounts instead
724
+ if not extracted_data:
725
+ # Pattern for dollar amounts: "Hardware: $50,000"
726
+ amount_pattern = r'([^:]+):\s*\$?([\d,]+)'
727
+ matches = re.finditer(amount_pattern, budget_text)
728
+
729
+ total_amount = 0
730
+ temp_data = []
731
+
732
+ for match in matches:
733
+ if len(match.groups()) >= 2:
734
+ category = match.group(1).strip()
735
+ try:
736
+ amount = int(match.group(2).replace(',', ''))
737
+ total_amount += amount
738
+ temp_data.append((category, amount))
739
+ except ValueError:
740
+ continue
741
+
742
+ # Convert absolute amounts to percentages
743
+ if total_amount > 0:
744
+ for category, amount in temp_data:
745
+ percentage = round((amount / total_amount) * 100)
746
+ extracted_data.append((category, percentage))
747
+
748
+ # If still no data extracted, create example data
749
+ if not extracted_data:
750
+ return [
751
+ ("Hardware", 30),
752
+ ("Software", 25),
753
+ ("Personnel", 35),
754
+ ("Training", 10),
755
+ ("Contingency", 10)
756
+ ]
757
+
758
+ return extracted_data
759
+
760
+ def extract_risk_data(proposal_text):
761
+ """Extract risk assessment data from the proposal text"""
762
+
763
+ # Look for risk section
764
+ risk_section = re.search(r'(?i)Risk Assessment.*?(?=\n\n[A-Z]|$)', proposal_text, re.DOTALL)
765
+ if not risk_section:
766
+ # Generate example data if no real data found
767
+ return [
768
+ ("Resource availability constraints", "Medium", "High"),
769
+ ("Technology integration issues", "High", "Medium"),
770
+ ("Budget overruns", "Medium", "High"),
771
+ ("Schedule delays", "High", "Medium"),
772
+ ("Stakeholder resistance", "Medium", "Medium")
773
+ ]
774
+
775
+ risk_text = risk_section.group(0)
776
+
777
+ # Split into lines
778
+ lines = risk_text.split('\n')
779
+ extracted_data = []
780
+
781
+ # Look for risk items in various formats
782
+ for line in lines:
783
+ # Skip empty lines
784
+ if not line.strip():
785
+ continue
786
+
787
+ # Look for risk items with probability and impact
788
+ # Patterns:
789
+ # 1. "Risk: Resource constraints - Probability: Medium, Impact: High"
790
+ # 2. "Resource constraints (Medium probability, High impact)"
791
+ # 3. "- Resource constraints: Medium probability, High impact"
792
+
793
+ # Pattern 1
794
+ match = re.search(r'(?i)(?:Risk:)?\s*([^-]+)\s*-\s*Probability:\s*(\w+),\s*Impact:\s*(\w+)', line)
795
+ if match:
796
+ risk = match.group(1).strip()
797
+ probability = match.group(2).strip()
798
+ impact = match.group(3).strip()
799
+ extracted_data.append((risk, probability, impact))
800
+ continue
801
+
802
+ # Pattern 2
803
+ match = re.search(r'([^(]+)\s*\((\w+)\s*probability,\s*(\w+)\s*impact\)', line)
804
+ if match:
805
+ risk = match.group(1).strip()
806
+ probability = match.group(2).strip()
807
+ impact = match.group(3).strip()
808
+ extracted_data.append((risk, probability, impact))
809
+ continue
810
+
811
+ # Pattern 3
812
+ match = re.search(r'(?:-|\*)\s*([^:]+):\s*(\w+)\s*probability,\s*(\w+)\s*impact', line)
813
+ if match:
814
+ risk = match.group(1).strip()
815
+ probability = match.group(2).strip()
816
+ impact = match.group(3).strip()
817
+ extracted_data.append((risk, probability, impact))
818
+ continue
819
+
820
+ # If we couldn't extract structured data, look for bullet points and guess
821
+ if not extracted_data:
822
+ bullet_items = re.findall(r'(?:^|\n)(?:-|\*|•|\d+\.)\s*([^\n]+)', risk_text)
823
+ for item in bullet_items:
824
+ # Clean the item text
825
+ item = item.strip()
826
+
827
+ # Guess probability and impact based on keywords
828
+ probability = "Medium"
829
+ impact = "Medium"
830
+
831
+ # Check for probability indicators
832
+ if re.search(r'(?i)\b(?:high probability|likely|frequently|often|high chance)\b', item):
833
+ probability = "High"
834
+ elif re.search(r'(?i)\b(?:low probability|unlikely|rarely|seldom|small chance)\b', item):
835
+ probability = "Low"
836
+
837
+ # Check for impact indicators
838
+ if re.search(r'(?i)\b(?:high impact|severe|critical|major|significant)\b', item):
839
+ impact = "High"
840
+ elif re.search(r'(?i)\b(?:low impact|minor|minimal|negligible)\b', item):
841
+ impact = "Low"
842
+
843
+ # Extract the risk description (removing any probability/impact text)
844
+ risk = re.sub(r'(?i)\b(?:high|medium|low)(?:\s+(?:probability|impact|chance|risk))\b', '', item)
845
+ risk = re.sub(r'(?i)\b(?:likely|unlikely|critical|severe|major|minor)\b', '', risk)
846
+ risk = risk.strip()
847
+
848
+ if risk:
849
+ extracted_data.append((risk, probability, impact))
850
+
851
+ # If still no data extracted, create example data
852
+ if not extracted_data:
853
+ return [
854
+ ("Resource availability constraints", "Medium", "High"),
855
+ ("Technology integration issues", "High", "Medium"),
856
+ ("Budget overruns", "Medium", "High"),
857
+ ("Schedule delays", "High", "Medium"),
858
+ ("Stakeholder resistance", "Medium", "Medium")
859
+ ]
860
+
861
+ return extracted_data
862
+
863
+ def get_risk_level(text):
864
+ """Convert text risk level to numeric value"""
865
+ text = text.lower()
866
+ if "high" in text:
867
+ return 3
868
+ elif "medium" in text:
869
+ return 2
870
+ else:
871
+ return 1 # Low or default
872
+
873
+ # ===== ANALYSIS AND UTILITY FUNCTIONS ===== #
874
+
875
+ def analyze_proposal(proposal_text):
876
+ """Analyze the generated proposal for metrics and structure"""
877
+ # Count words
878
+ word_count = len(proposal_text.split())
879
+
880
+ # Count sections
881
+ sections = [
882
+ "Executive Summary", "Project Background", "Goals and Objectives",
883
+ "Methodology", "Timeline", "Budget", "Expected Outcomes",
884
+ "Team and Resources", "Risk Assessment", "Conclusion"
885
+ ]
886
+ section_count = 0
887
+ for section in sections:
888
+ if re.search(rf'\b{section}\b', proposal_text, re.IGNORECASE):
889
+ section_count += 1
890
+
891
+ # Calculate reading time (average 200 words per minute)
892
+ reading_time_min = word_count / 200
893
+
894
+ # Calculate readability (simple algorithm based on word and sentence length)
895
+ sentences = re.split(r'[.!?]+', proposal_text)
896
+ sentence_count = len([s for s in sentences if s.strip()])
897
+ avg_sentence_length = word_count / max(1, sentence_count)
898
+
899
+ # Simplified Flesch-Kincaid calculation
900
+ readability_score = 206.835 - (1.015 * avg_sentence_length) - (84.6 * 1.5 / avg_sentence_length)
901
+ readability_score = max(0, min(100, readability_score))
902
+
903
+ # Analyze keyword presence for common business terms
904
+ keywords = {
905
+ "Strategic": proposal_text.lower().count("strategic"),
906
+ "Innovation": proposal_text.lower().count("innovat"),
907
+ "Efficiency": proposal_text.lower().count("efficien"),
908
+ "ROI": proposal_text.lower().count("roi") + proposal_text.lower().count("return on investment"),
909
+ "Stakeholder": proposal_text.lower().count("stakeholder"),
910
+ "Sustainable": proposal_text.lower().count("sustainab"),
911
+ }
912
+
913
+ # Return analysis results
914
+ return {
915
+ "word_count": word_count,
916
+ "section_count": section_count,
917
+ "readability_score": round(readability_score),
918
+ "estimated_reading_time": f"{reading_time_min:.1f} minutes",
919
+ "keyword_analysis": keywords,
920
+ "sections_present": {section: (1 if re.search(rf'\b{section}\b', proposal_text, re.IGNORECASE) else 0) for section in sections},
921
+ }
922
+
923
+ def create_slide_preview(pptx_path):
924
+ """Create a preview image of the first slide of the presentation"""
925
+ try:
926
+ # If using Windows with COM support, try this method
927
+ try:
928
+ from pptx import Presentation
929
+ import win32com.client
930
+ import os
931
+
932
+ # Get absolute path
933
+ abs_path = os.path.abspath(pptx_path)
934
+
935
+ # Export first slide as image using PowerPoint COM object
936
+ ppt_app = win32com.client.Dispatch('PowerPoint.Application')
937
+ presentation = ppt_app.Presentations.Open(abs_path)
938
+
939
+ # Save the first slide as PNG
940
+ output_path = abs_path.replace('.pptx', '_preview.png')
941
+ presentation.Slides[1].Export(output_path, 'PNG')
942
+
943
+ # Clean up
944
+ presentation.Close()
945
+ ppt_app.Quit()
946
+
947
+ return output_path
948
+ except:
949
+ # Fallback: return the PPT file path itself as we can't generate a preview
950
+ return pptx_path
951
+
952
+ except Exception as e:
953
+ print(f"Error creating slide preview: {e}")
954
+ # Return the PPT file path itself as we can't generate a preview
955
+ return pptx_path
956
+
957
+ def load_model():
958
+ """Load the model and tokenizer"""
959
+ print(f"Loading model: {MODEL_NAME}...")
960
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
961
+ model = AutoModelForCausalLM.from_pretrained(
962
+ MODEL_NAME,
963
+ torch_dtype=torch.float16 if device == "cuda" else torch.float32,
964
+ low_cpu_mem_usage=True,
965
+ device_map="auto"
966
+ )
967
+ return model, tokenizer
968
+
969
+ def process_input(title, description, temperature=0.7, max_length=4000,
970
+ color_scheme="Professional Blue", include_visuals=True,
971
+ focus_areas=None):
972
+ """Process the input and generate both proposal and slides with advanced options"""
973
+ # Load model if not already loaded
974
+ global model, tokenizer
975
+ if 'model' not in globals() or model is None:
976
+ model, tokenizer = load_model()
977
+
978
+ # Set parameters based on input
979
+ temperature_val = float(temperature)
980
+ max_length_val = int(max_length)
981
+
982
+ # Modify prompt based on focus areas
983
+ focus_instruction = ""
984
+ if focus_areas and len(focus_areas) > 0:
985
+ focus_instruction = f"Pay special attention to these aspects: {', '.join(focus_areas)}."
986
+
987
+ # Generate the proposal
988
+ proposal = generate_proposal(model, tokenizer, description + " " + focus_instruction,
989
+ temperature=temperature_val, max_length=max_length_val)
990
+
991
+ # Create the slides
992
+ ppt_path = create_slides(proposal, title)
993
+
994
+ # Add charts and visuals if requested
995
+ if include_visuals:
996
+ prs = Presentation(ppt_path)
997
+ prs = add_charts_and_visuals(prs, proposal, include_visuals)
998
+ prs.save(ppt_path)
999
+
1000
+ # Create a preview image of the first slide
1001
+ preview_path = create_slide_preview(ppt_path)
1002
+
1003
+ # Analyze the proposal
1004
+ analysis = analyze_proposal(proposal)
1005
+
1006
+ return proposal, ppt_path, preview_path, analysis
1007
+
1008
+ # ===== GRADIO INTERFACE FUNCTIONS ===== #
1009
+
1010
+ def create_interface():
1011
+ """Create an enhanced Gradio interface with advanced options"""
1012
+
1013
+ # Define color schemes for PowerPoint
1014
+ color_schemes = {
1015
+ "Professional Blue": {"primary": "#0070C0", "secondary": "#00B050"},
1016
+ "Elegant Gray": {"primary": "#404040", "secondary": "#7030A0"},
1017
+ "Bold Impact": {"primary": "#C00000", "secondary": "#FFC000"},
1018
+ "Modern Green": {"primary": "#00B050", "secondary": "#5B9BD5"},
1019
+ "Corporate Purple": {"primary": "#7030A0", "secondary": "#ED7D31"}
1020
+ }
1021
+
1022
+ with gr.Blocks(title="Advanced Project Proposal Generator", theme=gr.themes.Soft()) as app:
1023
+ gr.Markdown("# Professional Project Proposal Generator")
1024
+ gr.Markdown("#### Transform your idea into a complete project proposal with presentation slides")
1025
+
1026
+ with gr.Row():
1027
+ with gr.Column(scale=2):
1028
+ # Input section
1029
+ project_title = gr.Textbox(
1030
+ label="Project Title",
1031
+ placeholder="Enter your project title",
1032
+ value="New Project Proposal"
1033
+ )
1034
+
1035
+ description_input = gr.Textbox(
1036
+ label="Project Description",
1037
+ placeholder="Describe your project in detail. Include the purpose, scope, stakeholders, and any specific requirements or challenges...",
1038
+ lines=10
1039
+ )
1040
+
1041
+ with gr.Accordion("Advanced Options", open=False):
1042
+ with gr.Row():
1043
+ temperature_slider = gr.Slider(
1044
+ minimum=0.1,
1045
+ maximum=1.0,
1046
+ value=0.7,
1047
+ step=0.1,
1048
+ label="Creativity Level"
1049
+ )
1050
+
1051
+ max_length_slider = gr.Slider(
1052
+ minimum=2000,
1053
+ maximum=6000,
1054
+ value=4000,
1055
+ step=500,
1056
+ label="Maximum Length"
1057
+ )
1058
+
1059
+ with gr.Row():
1060
+ color_scheme = gr.Dropdown(
1061
+ choices=list(color_schemes.keys()),
1062
+ value="Professional Blue",
1063
+ label="Presentation Style"
1064
+ )
1065
+
1066
+ include_visuals = gr.Checkbox(
1067
+ label="Include Sample Charts/Diagrams",
1068
+ value=True
1069
+ )
1070
+
1071
+ with gr.Row():
1072
+ focus_areas = gr.CheckboxGroup(
1073
+ choices=[
1074
+ "Technical Details",
1075
+ "Business Impact",
1076
+ "Implementation Plan",
1077
+ "Cost Analysis",
1078
+ "Risk Management"
1079
+ ],
1080
+ value=["Business Impact", "Implementation Plan"],
1081
+ label="Focus Areas (Emphasize these aspects)"
1082
+ )
1083
+
1084
+ with gr.Row():
1085
+ clear_button = gr.Button("Clear All", variant="secondary")
1086
+ example_button = gr.Button("Load Example", variant="secondary")
1087
+ generate_button = gr.Button("Generate Proposal & Slides", variant="primary")
1088
+
1089
+ with gr.Column(scale=3):
1090
+ # Output tabs
1091
+ with gr.Tabs():
1092
+ with gr.TabItem("Proposal Text"):
1093
+ proposal_output = gr.Textbox(
1094
+ label="Generated Proposal",
1095
+ lines=25,
1096
+ show_copy_button=True
1097
+ )
1098
+
1099
+ with gr.TabItem("PowerPoint Preview"):
1100
+ gr.Markdown("#### PowerPoint Slides Preview")
1101
+ slides_preview = gr.Image(
1102
+ label="Preview (first slide)",
1103
+ type="filepath",
1104
+ height=400
1105
+ )
1106
+ slides_output = gr.File(
1107
+ label="Download Complete Presentation"
1108
+ )
1109
+
1110
+ with gr.TabItem("Proposal Analysis"):
1111
+ analysis_output = gr.JSON(
1112
+ label="Proposal Structure Analysis"
1113
+ )
1114
+
1115
+ # Example project description
1116
+ example_description = """
1117
+ Our company needs to implement a new customer relationship management (CRM) system to replace our outdated solution. The current system is 8 years old and lacks modern features like cloud integration, mobile access, and AI-powered analytics. We have approximately 5,000 customer records that need to be migrated, and 75 employees across sales, marketing, and customer service departments who will use the system. The project should include software selection, data migration, staff training, and integration with our existing ERP system. Budget constraints are significant, with a maximum allocation of $250,000. The implementation needs to be completed within 6 months to align with our fiscal year planning.
1118
+ """
1119
+
1120
+ # Set up event handlers
1121
+ def load_example():
1122
+ return "Enterprise CRM Implementation Project", example_description
1123
+
1124
+ example_button.click(
1125
+ load_example,
1126
+ outputs=[project_title, description_input]
1127
+ )
1128
+
1129
+ clear_button.click(
1130
+ lambda: ("", ""),
1131
+ outputs=[project_title, description_input]
1132
+ )
1133
+
1134
+ generate_button.click(
1135
+ process_input,
1136
+ inputs=[
1137
+ project_title,
1138
+ description_input,
1139
+ temperature_slider,
1140
+ max_length_slider,
1141
+ color_scheme,
1142
+ include_visuals,
1143
+ focus_areas
1144
+ ],
1145
+ outputs=[
1146
+ proposal_output,
1147
+ slides_output,
1148
+ slides_preview,
1149
+ analysis_output
1150
+ ]
1151
+ )
1152
+
1153
+ return app
1154
+
1155
+ # ===== MAIN SCRIPT CODE ===== #
1156
+
1157
+ # Global model and tokenizer
1158
+ model = None
1159
+ tokenizer = None
1160
+
1161
+ # Only load model at startup when running as main program
1162
+ if __name__ == "__main__":
1163
+ print("Loading initial model...")
1164
+ model, tokenizer = load_model()
1165
+ print("Starting Gradio interface...")
1166
+ app = create_interface()
1167
+ app.launch(share=True)