Proposal_gen_v1 / app.py
ans123's picture
Update app.py
67b74d5 verified
raw
history blame
15.8 kB
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)