File size: 15,849 Bytes
8cf2a7f
 
 
 
4558e9e
8cf2a7f
67b74d5
 
 
 
 
 
 
 
 
4558e9e
67b74d5
4558e9e
 
 
 
 
 
 
 
 
 
67b74d5
4558e9e
 
 
 
 
 
 
 
 
67b74d5
4558e9e
67b74d5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4558e9e
 
 
67b74d5
 
 
8cf2a7f
4558e9e
 
 
67b74d5
 
4558e9e
 
 
 
 
67b74d5
4558e9e
 
 
 
 
 
67b74d5
 
8cf2a7f
4558e9e
 
 
8cf2a7f
4558e9e
 
 
8cf2a7f
67b74d5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4558e9e
 
8cf2a7f
 
67b74d5
 
 
 
 
 
 
 
4558e9e
67b74d5
 
8cf2a7f
67b74d5
8cf2a7f
 
 
 
 
 
67b74d5
8cf2a7f
67b74d5
8cf2a7f
 
 
 
 
 
67b74d5
 
 
8cf2a7f
67b74d5
 
 
 
 
 
 
 
 
 
8cf2a7f
67b74d5
8cf2a7f
67b74d5
8cf2a7f
 
67b74d5
 
 
 
 
8cf2a7f
67b74d5
 
 
8cf2a7f
 
67b74d5
4558e9e
8cf2a7f
 
67b74d5
 
 
 
 
 
 
 
 
 
 
 
 
 
8cf2a7f
 
 
 
4558e9e
8cf2a7f
 
 
67b74d5
 
4558e9e
 
67b74d5
8cf2a7f
 
67b74d5
 
 
 
8cf2a7f
 
4558e9e
8cf2a7f
67b74d5
8cf2a7f
4558e9e
8cf2a7f
4558e9e
8cf2a7f
67b74d5
8cf2a7f
 
4558e9e
8cf2a7f
 
4558e9e
8cf2a7f
 
67b74d5
 
 
 
 
 
 
4558e9e
8cf2a7f
4558e9e
 
 
67b74d5
 
 
4558e9e
67b74d5
4558e9e
 
 
67b74d5
 
 
 
 
 
 
 
8cf2a7f
 
 
67b74d5
 
8cf2a7f
4558e9e
 
8cf2a7f
4558e9e
8cf2a7f
 
 
 
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
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
import os
import sys
import time
import gradio as gr
from openai import OpenAI
from pptx import Presentation
from pptx.util import Pt, Inches
from pptx.enum.text import PP_ALIGN
from pptx.dml.color import RGBColor
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY
from dotenv import load_dotenv
import re

# Load environment variables
load_dotenv()

# Get API key from environment variable
OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
if not OPENROUTER_API_KEY:
    raise ValueError("OPENROUTER_API_KEY environment variable is not set")

# OpenRouter API configuration
MODEL_NAME = "meta-llama/llama-3.3-8b-instruct:free"  # You can also use more powerful models like "anthropic/claude-3-opus-20240229"
SITE_URL = "https://proposal-generator.io"  # Replace with your actual site URL
SITE_NAME = "Professional Proposal Generator"  # Replace with your actual site name

# Initialize OpenAI client for OpenRouter
client = OpenAI(
    base_url="https://openrouter.ai/api/v1",
    api_key=OPENROUTER_API_KEY,
)

def generate_proposal(description, project_type=None):
    """Generate a proposal from a description using OpenRouter API"""
    
    # Create a better prompt with example formatting and detailed instructions
    system_prompt = """You are a professional business proposal writer with expertise in creating detailed, well-structured project proposals. 
    
Your proposals are comprehensive, highly detailed, and follow a clear structure with proper formatting. Each section should include substantial content with specific details, bullet points where appropriate, and professional language.

Make sure to:
1. Include proper formatting with section headings in bold
2. Add bullet points for lists and key points
3. Include realistic timelines, budgets, and team structures
4. Provide concrete, specific details rather than generic statements
5. Maintain a professional tone throughout
6. Create content that looks like it was written by human experts in the field

The proposal must be highly detailed, professionally formatted, and ready for presentation to stakeholders.
"""

    example_format = """**Project Proposal: [Title based on description]**

**1. Executive Summary**
A comprehensive 3-5 paragraph summary that clearly articulates the project's purpose, key features, benefits, timeline, budget range, and expected outcomes. Include specific metrics and goals.

**2. Project Background**
Detailed explanation of the current situation, market analysis, problems being addressed, and the opportunity. Include specific industry trends, challenges, and the gap this project fills.

**3. Goals and Objectives**
Clearly separated primary goals (broader aims) and specific objectives (measurable targets). Use bullet points for clarity, with each objective being SMART (Specific, Measurable, Achievable, Relevant, Time-bound).

**4. Methodology and Approach**
Detailed explanation of the implementation approach, including specific phases, methodologies, technologies, and frameworks to be used. Outline the specific steps that will be taken.

**5. Timeline**
Specific weekly breakdown of the project with clear milestones, deliverables, and dependencies. Include duration estimates and key decision points.

**6. Budget Considerations**
Detailed budget breakdown with percentages, actual figures, and justifications for each expense category. Include contingency planning and ROI estimates where applicable.

**7. Expected Outcomes**
Comprehensive list of both tangible and intangible outcomes, specific metrics for success, and long-term benefits. Include KPIs that will be used to measure success.

**8. Team and Resources**
Specific team composition with roles, responsibilities, and experience levels. Include external resources, tools, and infrastructure requirements.

**9. Risk Assessment**
Detailed identification of potential risks, probability and impact ratings, and specific mitigation strategies for each risk. Include contingency plans.

**10. Conclusion**
Powerful closing that reinforces the value proposition, summarizes key benefits, and includes a clear call to action."""

    # Create a project type specific prompt if provided
    project_specific_prompt = ""
    if project_type == "saas_performance":
        project_specific_prompt = """For this SaaS Performance Evaluation Platform, be sure to include:
- Multi-tenant architecture with role-based access control details
- User authentication and authorization specifics
- Dashboard and analytics features with visualization options
- Evaluation framework customization capabilities
- Data input methods and integration with external systems
- Performance tracking and reporting mechanisms
- Specific technology stack recommendations with justifications
- Security measures for protecting sensitive performance data
- Scalability and performance considerations"""
    
    prompt = f"""Create a detailed project proposal based on the following description. Format it exactly like the example format provided, with bold section headings and proper structure.

{example_format}

Project Description: {description}

{project_specific_prompt}

Create a complete, professionally formatted project proposal that could be presented directly to stakeholders. Make each section highly detailed and specific."""

    try:
        completion = client.chat.completions.create(
            extra_headers={
                "HTTP-Referer": SITE_URL,
                "X-Title": SITE_NAME,
            },
            model=MODEL_NAME,
            messages=[
                {
                    "role": "system",
                    "content": system_prompt
                },
                {
                    "role": "user",
                    "content": prompt
                }
            ],
            temperature=0.5,  # Lower temperature for more consistent results
            max_tokens=4500
        )
        
        proposal = completion.choices[0].message.content
        return proposal
    
    except Exception as e:
        print(f"Error generating proposal: {e}")
        return f"Error generating proposal: {e}"

def extract_title(proposal):
    """Extract the title from the proposal"""
    title_match = re.search(r"\*\*Project Proposal: (.*?)\*\*", proposal)
    if title_match:
        return title_match.group(1)
    return "Project Proposal"

def create_pdf(proposal, output_path="proposal.pdf"):
    """Create a PDF document from the proposal"""
    doc = SimpleDocTemplate(output_path, pagesize=letter,
                            rightMargin=72, leftMargin=72,
                            topMargin=72, bottomMargin=72)
    
    styles = getSampleStyleSheet()
    
    # Create custom styles
    styles.add(ParagraphStyle(name='Title',
                              parent=styles['Heading1'],
                              fontSize=16,
                              alignment=TA_CENTER,
                              spaceAfter=20))
    
    styles.add(ParagraphStyle(name='SectionHeading',
                              parent=styles['Heading2'],
                              fontSize=14,
                              spaceAfter=12,
                              spaceBefore=20))
    
    styles.add(ParagraphStyle(name='Normal',
                              parent=styles['Normal'],
                              fontSize=10,
                              alignment=TA_JUSTIFY,
                              spaceAfter=10))
    
    # Extract title
    title = extract_title(proposal)
    
    # Process the proposal content
    story = []
    
    # Add title
    story.append(Paragraph(f"<b>Project Proposal: {title}</b>", styles['Title']))
    story.append(Spacer(1, 0.25*inch))
    
    # Process sections
    sections = re.split(r'\*\*\d+\.\s+(.*?)\*\*', proposal)
    headers = re.findall(r'\*\*\d+\.\s+(.*?)\*\*', proposal)
    
    # The first element in sections is the title area, skip it
    for i, content in enumerate(sections[1:], 0):
        if i < len(headers):
            # Add section header
            story.append(Paragraph(f"<b>{i+1}. {headers[i]}</b>", styles['SectionHeading']))
            
            # Process content paragraphs
            paragraphs = content.strip().split('\n\n')
            for para in paragraphs:
                para = para.strip()
                if not para:
                    continue
                
                # Check if it's a bullet point list
                if re.match(r'^[\*\-]', para):
                    # Process bullet points
                    bullet_items = re.split(r'\n[\*\-]\s+', para)
                    for item in bullet_items:
                        item = item.strip()
                        if item:
                            # Remove leading bullet if present
                            item = re.sub(r'^[\*\-]\s+', '', item)
                            story.append(Paragraph(f"• {item}", styles['Normal']))
                else:
                    # Regular paragraph
                    story.append(Paragraph(para, styles['Normal']))
    
    # Build the PDF
    doc.build(story)
    return output_path

def create_slides(proposal):
    """Create PowerPoint slides from the proposal"""
    prs = Presentation()
    
    # Extract title
    title = extract_title(proposal)
    
    # Set up slide layouts
    title_slide_layout = prs.slide_layouts[0]
    section_title_layout = prs.slide_layouts[2]
    content_layout = prs.slide_layouts[1]
    
    # Add title slide
    title_slide = prs.slides.add_slide(title_slide_layout)
    title_slide.shapes.title.text = f"Project Proposal: {title}"
    subtitle = title_slide.placeholders[1]
    subtitle.text = "Professional Project Proposal"
    
    # List of sections to look for
    sections = [
        "Executive Summary", 
        "Project Background", 
        "Goals and Objectives", 
        "Methodology and Approach", 
        "Timeline", 
        "Budget Considerations", 
        "Expected Outcomes", 
        "Team and Resources", 
        "Risk Assessment", 
        "Conclusion"
    ]
    
    # Extract sections using regex
    section_matches = re.finditer(r'\*\*\d+\.\s+(.*?)\*\*\n\n(.*?)(?=\*\*\d+\.|\Z)', 
                                 proposal, re.DOTALL)
    
    for match in section_matches:
        section_title = match.group(1)
        section_content = match.group(2).strip()
        
        # Add section title slide
        section_slide = prs.slides.add_slide(section_title_layout)
        section_slide.shapes.title.text = section_title
        
        # Split content into paragraphs
        paragraphs = section_content.split('\n\n')
        
        # Process each paragraph
        current_slide = None
        text_frame = None
        paragraphs_on_slide = 0
        
        for para in paragraphs:
            para = para.strip()
            if not para:
                continue
            
            # Start a new slide if needed
            if current_slide is None or paragraphs_on_slide >= 3:
                current_slide = prs.slides.add_slide(content_layout)
                current_slide.shapes.title.text = section_title
                text_frame = current_slide.placeholders[1].text_frame
                paragraphs_on_slide = 0
            
            # Add the paragraph
            p = text_frame.add_paragraph()
            
            # Check if it's a bullet point list
            if re.match(r'^[\*\-]', para):
                # Process bullet points
                bullet_items = re.split(r'\n[\*\-]\s+', para)
                for item in bullet_items:
                    item = item.strip()
                    if item:
                        # Remove leading bullet if present
                        item = re.sub(r'^[\*\-]\s+', '', item)
                        bullet_p = text_frame.add_paragraph()
                        bullet_p.text = item
                        bullet_p.level = 1
            else:
                p.text = para
            
            paragraphs_on_slide += 1
    
    # Save the presentation
    output_path = "proposal_slides.pptx"
    prs.save(output_path)
    return output_path

def process_input(description, project_type):
    """Process the input and generate both proposal, PDF and slides"""
    # Check if input is too short
    if len(description.strip()) < 10:
        return "Please provide a more detailed project description (at least 10 characters).", None, None
    
    # Generate the proposal
    proposal = generate_proposal(description, project_type)
    
    # Create the PDF
    pdf_path = create_pdf(proposal)
    
    # Create the slides
    ppt_path = create_slides(proposal)
    
    return proposal, pdf_path, ppt_path

# Create Gradio interface
def create_interface():
    with gr.Blocks(title="Professional Project Proposal Generator") as app:
        gr.Markdown("# Professional Project Proposal Generator")
        gr.Markdown("Generate comprehensive, professionally formatted project proposals with PDF and PowerPoint exports.")
        
        with gr.Row():
            with gr.Column(scale=1):
                description_input = gr.Textbox(
                    label="Project Description",
                    placeholder="Describe your project in detail...",
                    lines=10
                )
                
                project_type = gr.Dropdown(
                    label="Project Type",
                    choices=["General Project", "SaaS Performance Platform"],
                    value="General Project"
                )
                
                generate_button = gr.Button("Generate Proposal", variant="primary")
                
                # Examples
                examples = gr.Examples(
                    examples=[
                        ["Develop a cloud-based SaaS platform for performance evaluation in educational institutions and corporate environments. The platform will enable users to track, evaluate, and report on individual performance metrics with customizable evaluation models.", "SaaS Performance Platform"],
                        ["Create a mobile application for sustainable waste management and recycling in urban communities. The app will connect residents with local recycling centers and provide educational resources on waste reduction.", "General Project"],
                        ["Design and implement a smart agriculture system using IoT sensors for small-scale farms. The system will monitor soil conditions, weather patterns, and crop health to optimize irrigation and fertilization.", "General Project"]
                    ],
                    inputs=[description_input, project_type]
                )
            
            with gr.Column(scale=2):
                output_tabs = gr.Tabs()
                with output_tabs:
                    with gr.TabItem("Proposal Text"):
                        proposal_output = gr.Textbox(label="Generated Proposal", lines=25)
                    with gr.TabItem("Documents"):
                        with gr.Row():
                            pdf_output = gr.File(label="PDF Document")
                            slides_output = gr.File(label="PowerPoint Slides")
        
        generate_button.click(
            process_input,
            inputs=[description_input, project_type],
            outputs=[proposal_output, pdf_output, slides_output]
        )
        
        return app

# Main script code
if __name__ == "__main__":
    print("Starting Gradio interface...")
    app = create_interface()
    app.launch(share=True)