""" Vero Template Generator Backend Flask API for generating professional documents using Gemini AI """ import io import os import sys from datetime import datetime from flask import Flask, jsonify, request, send_file from flask_cors import CORS from utils.docx_generator import get_docx_generator from utils.gemini_client import get_gemini_client from utils.pdf_generator import get_pdf_generator # Initialize Flask app app = Flask(__name__) CORS(app) # Initialize services try: gemini_client = get_gemini_client() docx_generator = get_docx_generator() pdf_generator = get_pdf_generator() print("✓ All services initialized successfully", file=sys.stderr) except Exception as e: print(f"✗ Error initializing services: {str(e)}", file=sys.stderr) sys.exit(1) # ============================================================================ # HEALTH CHECK # ============================================================================ @app.route("/health", methods=["GET"]) def health_check(): """Health check endpoint""" return jsonify( { "status": "healthy", "service": "Vero Template Generator", "version": "1.0.0", "timestamp": datetime.now().isoformat(), "endpoints": [ "/generate-resume", "/generate-cover-letter", "/generate-proposal", "/generate-invoice", "/generate-contract", "/generate-portfolio-pdf", "/enhance-description", "/enhance-skills-summary", ], } ) # ============================================================================ # RESUME GENERATOR # ============================================================================ @app.route("/generate-resume", methods=["POST"]) def generate_resume(): """ Generate professional resume Expected JSON: { "personal_info": { "name": "John Doe", "email": "john@example.com", "phone": "+1234567890", "location": "City, State", "linkedin": "linkedin.com/in/johndoe", "website": "johndoe.com" }, "summary": "Professional summary text", "experience": [ { "title": "Senior Developer", "company": "Tech Corp", "location": "City, State", "start_date": "Jan 2020", "end_date": "Present", "responsibilities": ["Task 1", "Task 2"] } ], "education": [ { "degree": "Bachelor of Science", "field": "Computer Science", "school": "University Name", "graduation_date": "May 2019", "gpa": "3.8", "honors": "Magna Cum Laude" } ], "skills": ["Python", "JavaScript", "React"], "certifications": [ { "name": "AWS Certified", "issuer": "Amazon", "date": "2023" } ], "projects": [ { "name": "Project Name", "description": "Description", "technologies": ["Tech1", "Tech2"] } ], "enhance_with_ai": true } """ try: data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 # Optional: Enhance descriptions with AI if data.get("enhance_with_ai", False): print("Enhancing resume content with AI...", file=sys.stderr) # Enhance professional summary if data.get("summary"): skills = data.get("skills", []) if isinstance(skills, dict): skills = [s for skill_list in skills.values() for s in skill_list] data["summary"] = gemini_client.generate_skills_summary( skills, data.get("years_experience", 0) ) # Enhance work experience descriptions if data.get("experience"): for exp in data["experience"]: if exp.get("responsibilities"): enhanced_resps = [] for resp in exp["responsibilities"]: try: enhanced = gemini_client.enhance_resume_description( resp, exp.get("title", "") ) enhanced_resps.append(enhanced) except Exception as e: print( f"Error enhancing responsibility: {str(e)}", file=sys.stderr, ) enhanced_resps.append(resp) exp["responsibilities"] = enhanced_resps # Enhance project descriptions if data.get("projects"): for proj in data["projects"]: if proj.get("description"): try: enhanced = gemini_client.enhance_portfolio_description(proj) proj["description"] = enhanced except Exception as e: print(f"Error enhancing project: {str(e)}", file=sys.stderr) # Generate DOCX print("Generating resume document...", file=sys.stderr) docx_buffer = docx_generator.generate_resume(data) # Prepare filename name = data.get("personal_info", {}).get("name", "Resume") filename = f"{name.replace(' ', '_')}_Resume.docx" return send_file( docx_buffer, mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document", as_attachment=True, download_name=filename, ) except Exception as e: print(f"Error generating resume: {str(e)}", file=sys.stderr) return jsonify({"error": str(e)}), 500 # ============================================================================ # COVER LETTER GENERATOR # ============================================================================ @app.route("/generate-cover-letter", methods=["POST"]) def generate_cover_letter(): """ Generate personalized cover letter Expected JSON: { "name": "John Doe", "address": "123 Main St, City, State", "email": "john@example.com", "phone": "+1234567890", "date": "January 15, 2024", "company": "Tech Corp", "hiring_manager": "Jane Smith", "position": "Senior Developer", "skills": ["Python", "React", "AWS"], "experience": "5 years of full-stack development", "tone": "formal", "custom_content": "Optional pre-written content", "generate_with_ai": true } """ try: data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 # Generate content with AI if requested if data.get("generate_with_ai", True) and not data.get("custom_content"): print("Generating cover letter content with AI...", file=sys.stderr) content = gemini_client.generate_cover_letter(data) data["content"] = content elif data.get("custom_content"): data["content"] = data["custom_content"] else: return ( jsonify( { "error": "Either generate_with_ai must be true or custom_content must be provided" } ), 400, ) # Generate DOCX print("Generating cover letter document...", file=sys.stderr) docx_buffer = docx_generator.generate_cover_letter(data) # Prepare filename name = data.get("name", "Applicant").replace(" ", "_") company = data.get("company", "Company").replace(" ", "_") filename = f"{name}_CoverLetter_{company}.docx" return send_file( docx_buffer, mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document", as_attachment=True, download_name=filename, ) except Exception as e: print(f"Error generating cover letter: {str(e)}", file=sys.stderr) return jsonify({"error": str(e)}), 500 # ============================================================================ # PROPOSAL GENERATOR # ============================================================================ @app.route("/generate-proposal", methods=["POST"]) def generate_proposal(): """ Generate business proposal Expected JSON: { "title": "Web Development Proposal", "client_name": "ABC Company", "prepared_by": "Your Company Name", "date": "January 15, 2024", "project_title": "E-commerce Website", "scope": "Develop a full-featured e-commerce platform", "deliverables": ["Website", "Admin Panel", "Mobile App"], "timeline": "3 months", "budget": "$50,000", "generate_with_ai": true, "custom_content": "Optional pre-written proposal" } """ try: data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 # Generate proposal content with AI if requested if data.get("generate_with_ai", True) and not data.get("custom_content"): print("Generating proposal content with AI...", file=sys.stderr) content = gemini_client.generate_proposal(data) data["content"] = content elif data.get("custom_content"): data["content"] = data["custom_content"] # Generate DOCX print("Generating proposal document...", file=sys.stderr) docx_buffer = docx_generator.generate_proposal(data) # Prepare filename client = data.get("client_name", "Client").replace(" ", "_") title = data.get("project_title", "Proposal").replace(" ", "_") filename = f"Proposal_{client}_{title}.docx" return send_file( docx_buffer, mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document", as_attachment=True, download_name=filename, ) except Exception as e: print(f"Error generating proposal: {str(e)}", file=sys.stderr) return jsonify({"error": str(e)}), 500 # ============================================================================ # INVOICE GENERATOR # ============================================================================ @app.route("/generate-invoice", methods=["POST"]) def generate_invoice(): """ Generate professional invoice Expected JSON: { "invoice_number": "INV-001", "invoice_date": "2024-01-15", "due_date": "2024-02-15", "from_info": { "name": "Your Business", "address": "123 Business St", "email": "billing@business.com", "phone": "+1234567890" }, "to_info": { "name": "Client Name", "address": "456 Client Ave", "email": "client@example.com" }, "items": [ { "description": "Service/Product", "quantity": 1, "rate": 1000, "amount": 1000 } ], "tax_rate": 8.5, "discount": 0, "notes": "Thank you for your business", "payment_instructions": "Payment via bank transfer" } """ try: data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 # Validate required fields if not data.get("items"): return jsonify({"error": "Invoice items are required"}), 400 # Generate DOCX print("Generating invoice document...", file=sys.stderr) docx_buffer = docx_generator.generate_invoice(data) # Prepare filename invoice_num = data.get("invoice_number", "INV-001").replace("/", "-") filename = f"Invoice_{invoice_num}.docx" return send_file( docx_buffer, mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document", as_attachment=True, download_name=filename, ) except Exception as e: print(f"Error generating invoice: {str(e)}", file=sys.stderr) return jsonify({"error": str(e)}), 500 # ============================================================================ # CONTRACT GENERATOR # ============================================================================ @app.route("/generate-contract", methods=["POST"]) def generate_contract(): """ Generate legal contract Expected JSON: { "contract_type": "Freelance Service Agreement", "date": "January 15, 2024", "party1": { "name": "Service Provider", "address": "123 Provider St" }, "party2": { "name": "Client Name", "address": "456 Client Ave" }, "effective_date": "January 20, 2024", "expiration_date": "December 31, 2024", "custom_terms": "Net 30 payment terms", "generate_with_ai": true, "custom_content": "Optional pre-written terms" } """ try: data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 # Generate contract terms with AI if requested if data.get("generate_with_ai", True) and not data.get("custom_content"): print("Generating contract terms with AI...", file=sys.stderr) contract_type = data.get("contract_type", "Service Agreement") custom_terms = data.get("custom_terms", "") terms = gemini_client.enhance_contract_terms(contract_type, custom_terms) data["terms"] = terms elif data.get("custom_content"): data["terms"] = data["custom_content"] # Generate DOCX print("Generating contract document...", file=sys.stderr) docx_buffer = docx_generator.generate_contract(data) # Prepare filename contract_type = data.get("contract_type", "Contract").replace(" ", "_") filename = f"{contract_type}_Contract.docx" return send_file( docx_buffer, mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document", as_attachment=True, download_name=filename, ) except Exception as e: print(f"Error generating contract: {str(e)}", file=sys.stderr) return jsonify({"error": str(e)}), 500 # ============================================================================ # PORTFOLIO PDF EXPORT # ============================================================================ @app.route("/generate-portfolio-pdf", methods=["POST"]) def generate_portfolio_pdf(): """ Generate portfolio PDF Expected JSON: { "name": "John Doe", "title": "Full Stack Developer", "bio": "Professional bio text", "contact": { "email": "john@example.com", "phone": "+1234567890", "website": "johndoe.com", "linkedin": "linkedin.com/in/johndoe" }, "skills": ["Python", "React", "AWS"], "experience": [...], "education": [...], "projects": [...], "certifications": [...], "enhance_with_ai": false } """ try: data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 # Optional AI enhancement if data.get("enhance_with_ai", False): print("Enhancing portfolio content with AI...", file=sys.stderr) # Enhance bio if data.get("bio"): data["bio"] = gemini_client.improve_text_quality( data["bio"], "professional" ) # Enhance project descriptions if data.get("projects"): for proj in data["projects"]: if proj.get("description"): try: enhanced = gemini_client.enhance_portfolio_description(proj) proj["description"] = enhanced except Exception as e: print(f"Error enhancing project: {str(e)}", file=sys.stderr) # Generate PDF print("Generating portfolio PDF...", file=sys.stderr) pdf_buffer = pdf_generator.generate_portfolio_pdf(data) # Prepare filename name = data.get("name", "Portfolio").replace(" ", "_") filename = f"{name}_Portfolio.pdf" return send_file( pdf_buffer, mimetype="application/pdf", as_attachment=True, download_name=filename, ) except Exception as e: print(f"Error generating portfolio PDF: {str(e)}", file=sys.stderr) return jsonify({"error": str(e)}), 500 # ============================================================================ # AI ENHANCEMENT UTILITIES # ============================================================================ @app.route("/enhance-description", methods=["POST"]) def enhance_description(): """ Enhance text description with AI Expected JSON: { "text": "Original text to enhance", "context": "resume/portfolio/proposal", "role": "Optional job role for context" } """ try: data = request.get_json() if not data or not data.get("text"): return jsonify({"error": "Text is required"}), 400 text = data["text"] context = data.get("context", "general") role = data.get("role", "") if context == "resume": enhanced = gemini_client.enhance_resume_description(text, role) elif context == "portfolio": project_data = { "title": role, "description": text, "technologies": data.get("technologies", []), "role": data.get("your_role", "Developer"), } enhanced = gemini_client.enhance_portfolio_description(project_data) else: enhanced = gemini_client.improve_text_quality(text, "professional") return jsonify({"original": text, "enhanced": enhanced, "success": True}) except Exception as e: print(f"Error enhancing description: {str(e)}", file=sys.stderr) return jsonify({"error": str(e)}), 500 @app.route("/enhance-skills-summary", methods=["POST"]) def enhance_skills_summary(): """ Generate professional skills summary Expected JSON: { "skills": ["Python", "React", "AWS"], "experience_years": 5 } """ try: data = request.get_json() if not data or not data.get("skills"): return jsonify({"error": "Skills list is required"}), 400 skills = data["skills"] years = data.get("experience_years", 0) summary = gemini_client.generate_skills_summary(skills, years) return jsonify({"skills": skills, "summary": summary, "success": True}) except Exception as e: print(f"Error generating skills summary: {str(e)}", file=sys.stderr) return jsonify({"error": str(e)}), 500 @app.route("/", methods=["GET"]) def index(): return jsonify({ "service": "Vero Template Generator", "message": "API is running", "health": "/health" }) # ============================================================================ # ERROR HANDLERS # ============================================================================ @app.errorhandler(404) def not_found(error): """Handle 404 errors""" return jsonify({"error": "Endpoint not found"}), 404 @app.errorhandler(500) def internal_error(error): """Handle 500 errors""" return jsonify({"error": "Internal server error"}), 500 # ============================================================================ # MAIN # ============================================================================ if __name__ == "__main__": port = int(os.environ.get("PORT", 7860)) app.run(host="0.0.0.0", port=port, debug=False)