omgy commited on
Commit
22e7783
·
verified ·
1 Parent(s): 1b8a9cb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +627 -619
app.py CHANGED
@@ -1,619 +1,627 @@
1
- """
2
- Vero Template Generator Backend
3
- Flask API for generating professional documents using Gemini AI
4
- """
5
-
6
- import io
7
- import os
8
- import sys
9
- from datetime import datetime
10
-
11
- from flask import Flask, jsonify, request, send_file
12
- from flask_cors import CORS
13
- from utils.docx_generator import get_docx_generator
14
- from utils.gemini_client import get_gemini_client
15
- from utils.pdf_generator import get_pdf_generator
16
-
17
- # Initialize Flask app
18
- app = Flask(__name__)
19
- CORS(app)
20
-
21
- # Initialize services
22
- try:
23
- gemini_client = get_gemini_client()
24
- docx_generator = get_docx_generator()
25
- pdf_generator = get_pdf_generator()
26
- print("✓ All services initialized successfully", file=sys.stderr)
27
- except Exception as e:
28
- print(f"✗ Error initializing services: {str(e)}", file=sys.stderr)
29
- sys.exit(1)
30
-
31
-
32
- # ============================================================================
33
- # HEALTH CHECK
34
- # ============================================================================
35
-
36
-
37
- @app.route("/health", methods=["GET"])
38
- def health_check():
39
- """Health check endpoint"""
40
- return jsonify(
41
- {
42
- "status": "healthy",
43
- "service": "Vero Template Generator",
44
- "version": "1.0.0",
45
- "timestamp": datetime.now().isoformat(),
46
- "endpoints": [
47
- "/generate-resume",
48
- "/generate-cover-letter",
49
- "/generate-proposal",
50
- "/generate-invoice",
51
- "/generate-contract",
52
- "/generate-portfolio-pdf",
53
- "/enhance-description",
54
- "/enhance-skills-summary",
55
- ],
56
- }
57
- )
58
-
59
-
60
- # ============================================================================
61
- # RESUME GENERATOR
62
- # ============================================================================
63
-
64
-
65
- @app.route("/generate-resume", methods=["POST"])
66
- def generate_resume():
67
- """
68
- Generate professional resume
69
- Expected JSON:
70
- {
71
- "personal_info": {
72
- "name": "John Doe",
73
- "email": "john@example.com",
74
- "phone": "+1234567890",
75
- "location": "City, State",
76
- "linkedin": "linkedin.com/in/johndoe",
77
- "website": "johndoe.com"
78
- },
79
- "summary": "Professional summary text",
80
- "experience": [
81
- {
82
- "title": "Senior Developer",
83
- "company": "Tech Corp",
84
- "location": "City, State",
85
- "start_date": "Jan 2020",
86
- "end_date": "Present",
87
- "responsibilities": ["Task 1", "Task 2"]
88
- }
89
- ],
90
- "education": [
91
- {
92
- "degree": "Bachelor of Science",
93
- "field": "Computer Science",
94
- "school": "University Name",
95
- "graduation_date": "May 2019",
96
- "gpa": "3.8",
97
- "honors": "Magna Cum Laude"
98
- }
99
- ],
100
- "skills": ["Python", "JavaScript", "React"],
101
- "certifications": [
102
- {
103
- "name": "AWS Certified",
104
- "issuer": "Amazon",
105
- "date": "2023"
106
- }
107
- ],
108
- "projects": [
109
- {
110
- "name": "Project Name",
111
- "description": "Description",
112
- "technologies": ["Tech1", "Tech2"]
113
- }
114
- ],
115
- "enhance_with_ai": true
116
- }
117
- """
118
- try:
119
- data = request.get_json()
120
-
121
- if not data:
122
- return jsonify({"error": "No data provided"}), 400
123
-
124
- # Optional: Enhance descriptions with AI
125
- if data.get("enhance_with_ai", False):
126
- print("Enhancing resume content with AI...", file=sys.stderr)
127
-
128
- # Enhance professional summary
129
- if data.get("summary"):
130
- skills = data.get("skills", [])
131
- if isinstance(skills, dict):
132
- skills = [s for skill_list in skills.values() for s in skill_list]
133
- data["summary"] = gemini_client.generate_skills_summary(
134
- skills, data.get("years_experience", 0)
135
- )
136
-
137
- # Enhance work experience descriptions
138
- if data.get("experience"):
139
- for exp in data["experience"]:
140
- if exp.get("responsibilities"):
141
- enhanced_resps = []
142
- for resp in exp["responsibilities"]:
143
- try:
144
- enhanced = gemini_client.enhance_resume_description(
145
- resp, exp.get("title", "")
146
- )
147
- enhanced_resps.append(enhanced)
148
- except Exception as e:
149
- print(
150
- f"Error enhancing responsibility: {str(e)}",
151
- file=sys.stderr,
152
- )
153
- enhanced_resps.append(resp)
154
- exp["responsibilities"] = enhanced_resps
155
-
156
- # Enhance project descriptions
157
- if data.get("projects"):
158
- for proj in data["projects"]:
159
- if proj.get("description"):
160
- try:
161
- enhanced = gemini_client.enhance_portfolio_description(proj)
162
- proj["description"] = enhanced
163
- except Exception as e:
164
- print(f"Error enhancing project: {str(e)}", file=sys.stderr)
165
-
166
- # Generate DOCX
167
- print("Generating resume document...", file=sys.stderr)
168
- docx_buffer = docx_generator.generate_resume(data)
169
-
170
- # Prepare filename
171
- name = data.get("personal_info", {}).get("name", "Resume")
172
- filename = f"{name.replace(' ', '_')}_Resume.docx"
173
-
174
- return send_file(
175
- docx_buffer,
176
- mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
177
- as_attachment=True,
178
- download_name=filename,
179
- )
180
-
181
- except Exception as e:
182
- print(f"Error generating resume: {str(e)}", file=sys.stderr)
183
- return jsonify({"error": str(e)}), 500
184
-
185
-
186
- # ============================================================================
187
- # COVER LETTER GENERATOR
188
- # ============================================================================
189
-
190
-
191
- @app.route("/generate-cover-letter", methods=["POST"])
192
- def generate_cover_letter():
193
- """
194
- Generate personalized cover letter
195
- Expected JSON:
196
- {
197
- "name": "John Doe",
198
- "address": "123 Main St, City, State",
199
- "email": "john@example.com",
200
- "phone": "+1234567890",
201
- "date": "January 15, 2024",
202
- "company": "Tech Corp",
203
- "hiring_manager": "Jane Smith",
204
- "position": "Senior Developer",
205
- "skills": ["Python", "React", "AWS"],
206
- "experience": "5 years of full-stack development",
207
- "tone": "formal",
208
- "custom_content": "Optional pre-written content",
209
- "generate_with_ai": true
210
- }
211
- """
212
- try:
213
- data = request.get_json()
214
-
215
- if not data:
216
- return jsonify({"error": "No data provided"}), 400
217
-
218
- # Generate content with AI if requested
219
- if data.get("generate_with_ai", True) and not data.get("custom_content"):
220
- print("Generating cover letter content with AI...", file=sys.stderr)
221
- content = gemini_client.generate_cover_letter(data)
222
- data["content"] = content
223
- elif data.get("custom_content"):
224
- data["content"] = data["custom_content"]
225
- else:
226
- return (
227
- jsonify(
228
- {
229
- "error": "Either generate_with_ai must be true or custom_content must be provided"
230
- }
231
- ),
232
- 400,
233
- )
234
-
235
- # Generate DOCX
236
- print("Generating cover letter document...", file=sys.stderr)
237
- docx_buffer = docx_generator.generate_cover_letter(data)
238
-
239
- # Prepare filename
240
- name = data.get("name", "Applicant").replace(" ", "_")
241
- company = data.get("company", "Company").replace(" ", "_")
242
- filename = f"{name}_CoverLetter_{company}.docx"
243
-
244
- return send_file(
245
- docx_buffer,
246
- mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
247
- as_attachment=True,
248
- download_name=filename,
249
- )
250
-
251
- except Exception as e:
252
- print(f"Error generating cover letter: {str(e)}", file=sys.stderr)
253
- return jsonify({"error": str(e)}), 500
254
-
255
-
256
- # ============================================================================
257
- # PROPOSAL GENERATOR
258
- # ============================================================================
259
-
260
-
261
- @app.route("/generate-proposal", methods=["POST"])
262
- def generate_proposal():
263
- """
264
- Generate business proposal
265
- Expected JSON:
266
- {
267
- "title": "Web Development Proposal",
268
- "client_name": "ABC Company",
269
- "prepared_by": "Your Company Name",
270
- "date": "January 15, 2024",
271
- "project_title": "E-commerce Website",
272
- "scope": "Develop a full-featured e-commerce platform",
273
- "deliverables": ["Website", "Admin Panel", "Mobile App"],
274
- "timeline": "3 months",
275
- "budget": "$50,000",
276
- "generate_with_ai": true,
277
- "custom_content": "Optional pre-written proposal"
278
- }
279
- """
280
- try:
281
- data = request.get_json()
282
-
283
- if not data:
284
- return jsonify({"error": "No data provided"}), 400
285
-
286
- # Generate proposal content with AI if requested
287
- if data.get("generate_with_ai", True) and not data.get("custom_content"):
288
- print("Generating proposal content with AI...", file=sys.stderr)
289
- content = gemini_client.generate_proposal(data)
290
- data["content"] = content
291
- elif data.get("custom_content"):
292
- data["content"] = data["custom_content"]
293
-
294
- # Generate DOCX
295
- print("Generating proposal document...", file=sys.stderr)
296
- docx_buffer = docx_generator.generate_proposal(data)
297
-
298
- # Prepare filename
299
- client = data.get("client_name", "Client").replace(" ", "_")
300
- title = data.get("project_title", "Proposal").replace(" ", "_")
301
- filename = f"Proposal_{client}_{title}.docx"
302
-
303
- return send_file(
304
- docx_buffer,
305
- mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
306
- as_attachment=True,
307
- download_name=filename,
308
- )
309
-
310
- except Exception as e:
311
- print(f"Error generating proposal: {str(e)}", file=sys.stderr)
312
- return jsonify({"error": str(e)}), 500
313
-
314
-
315
- # ============================================================================
316
- # INVOICE GENERATOR
317
- # ============================================================================
318
-
319
-
320
- @app.route("/generate-invoice", methods=["POST"])
321
- def generate_invoice():
322
- """
323
- Generate professional invoice
324
- Expected JSON:
325
- {
326
- "invoice_number": "INV-001",
327
- "invoice_date": "2024-01-15",
328
- "due_date": "2024-02-15",
329
- "from_info": {
330
- "name": "Your Business",
331
- "address": "123 Business St",
332
- "email": "billing@business.com",
333
- "phone": "+1234567890"
334
- },
335
- "to_info": {
336
- "name": "Client Name",
337
- "address": "456 Client Ave",
338
- "email": "client@example.com"
339
- },
340
- "items": [
341
- {
342
- "description": "Service/Product",
343
- "quantity": 1,
344
- "rate": 1000,
345
- "amount": 1000
346
- }
347
- ],
348
- "tax_rate": 8.5,
349
- "discount": 0,
350
- "notes": "Thank you for your business",
351
- "payment_instructions": "Payment via bank transfer"
352
- }
353
- """
354
- try:
355
- data = request.get_json()
356
-
357
- if not data:
358
- return jsonify({"error": "No data provided"}), 400
359
-
360
- # Validate required fields
361
- if not data.get("items"):
362
- return jsonify({"error": "Invoice items are required"}), 400
363
-
364
- # Generate DOCX
365
- print("Generating invoice document...", file=sys.stderr)
366
- docx_buffer = docx_generator.generate_invoice(data)
367
-
368
- # Prepare filename
369
- invoice_num = data.get("invoice_number", "INV-001").replace("/", "-")
370
- filename = f"Invoice_{invoice_num}.docx"
371
-
372
- return send_file(
373
- docx_buffer,
374
- mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
375
- as_attachment=True,
376
- download_name=filename,
377
- )
378
-
379
- except Exception as e:
380
- print(f"Error generating invoice: {str(e)}", file=sys.stderr)
381
- return jsonify({"error": str(e)}), 500
382
-
383
-
384
- # ============================================================================
385
- # CONTRACT GENERATOR
386
- # ============================================================================
387
-
388
-
389
- @app.route("/generate-contract", methods=["POST"])
390
- def generate_contract():
391
- """
392
- Generate legal contract
393
- Expected JSON:
394
- {
395
- "contract_type": "Freelance Service Agreement",
396
- "date": "January 15, 2024",
397
- "party1": {
398
- "name": "Service Provider",
399
- "address": "123 Provider St"
400
- },
401
- "party2": {
402
- "name": "Client Name",
403
- "address": "456 Client Ave"
404
- },
405
- "effective_date": "January 20, 2024",
406
- "expiration_date": "December 31, 2024",
407
- "custom_terms": "Net 30 payment terms",
408
- "generate_with_ai": true,
409
- "custom_content": "Optional pre-written terms"
410
- }
411
- """
412
- try:
413
- data = request.get_json()
414
-
415
- if not data:
416
- return jsonify({"error": "No data provided"}), 400
417
-
418
- # Generate contract terms with AI if requested
419
- if data.get("generate_with_ai", True) and not data.get("custom_content"):
420
- print("Generating contract terms with AI...", file=sys.stderr)
421
- contract_type = data.get("contract_type", "Service Agreement")
422
- custom_terms = data.get("custom_terms", "")
423
- terms = gemini_client.enhance_contract_terms(contract_type, custom_terms)
424
- data["terms"] = terms
425
- elif data.get("custom_content"):
426
- data["terms"] = data["custom_content"]
427
-
428
- # Generate DOCX
429
- print("Generating contract document...", file=sys.stderr)
430
- docx_buffer = docx_generator.generate_contract(data)
431
-
432
- # Prepare filename
433
- contract_type = data.get("contract_type", "Contract").replace(" ", "_")
434
- filename = f"{contract_type}_Contract.docx"
435
-
436
- return send_file(
437
- docx_buffer,
438
- mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
439
- as_attachment=True,
440
- download_name=filename,
441
- )
442
-
443
- except Exception as e:
444
- print(f"Error generating contract: {str(e)}", file=sys.stderr)
445
- return jsonify({"error": str(e)}), 500
446
-
447
-
448
- # ============================================================================
449
- # PORTFOLIO PDF EXPORT
450
- # ============================================================================
451
-
452
-
453
- @app.route("/generate-portfolio-pdf", methods=["POST"])
454
- def generate_portfolio_pdf():
455
- """
456
- Generate portfolio PDF
457
- Expected JSON:
458
- {
459
- "name": "John Doe",
460
- "title": "Full Stack Developer",
461
- "bio": "Professional bio text",
462
- "contact": {
463
- "email": "john@example.com",
464
- "phone": "+1234567890",
465
- "website": "johndoe.com",
466
- "linkedin": "linkedin.com/in/johndoe"
467
- },
468
- "skills": ["Python", "React", "AWS"],
469
- "experience": [...],
470
- "education": [...],
471
- "projects": [...],
472
- "certifications": [...],
473
- "enhance_with_ai": false
474
- }
475
- """
476
- try:
477
- data = request.get_json()
478
-
479
- if not data:
480
- return jsonify({"error": "No data provided"}), 400
481
-
482
- # Optional AI enhancement
483
- if data.get("enhance_with_ai", False):
484
- print("Enhancing portfolio content with AI...", file=sys.stderr)
485
-
486
- # Enhance bio
487
- if data.get("bio"):
488
- data["bio"] = gemini_client.improve_text_quality(
489
- data["bio"], "professional"
490
- )
491
-
492
- # Enhance project descriptions
493
- if data.get("projects"):
494
- for proj in data["projects"]:
495
- if proj.get("description"):
496
- try:
497
- enhanced = gemini_client.enhance_portfolio_description(proj)
498
- proj["description"] = enhanced
499
- except Exception as e:
500
- print(f"Error enhancing project: {str(e)}", file=sys.stderr)
501
-
502
- # Generate PDF
503
- print("Generating portfolio PDF...", file=sys.stderr)
504
- pdf_buffer = pdf_generator.generate_portfolio_pdf(data)
505
-
506
- # Prepare filename
507
- name = data.get("name", "Portfolio").replace(" ", "_")
508
- filename = f"{name}_Portfolio.pdf"
509
-
510
- return send_file(
511
- pdf_buffer,
512
- mimetype="application/pdf",
513
- as_attachment=True,
514
- download_name=filename,
515
- )
516
-
517
- except Exception as e:
518
- print(f"Error generating portfolio PDF: {str(e)}", file=sys.stderr)
519
- return jsonify({"error": str(e)}), 500
520
-
521
-
522
- # ============================================================================
523
- # AI ENHANCEMENT UTILITIES
524
- # ============================================================================
525
-
526
-
527
- @app.route("/enhance-description", methods=["POST"])
528
- def enhance_description():
529
- """
530
- Enhance text description with AI
531
- Expected JSON:
532
- {
533
- "text": "Original text to enhance",
534
- "context": "resume/portfolio/proposal",
535
- "role": "Optional job role for context"
536
- }
537
- """
538
- try:
539
- data = request.get_json()
540
-
541
- if not data or not data.get("text"):
542
- return jsonify({"error": "Text is required"}), 400
543
-
544
- text = data["text"]
545
- context = data.get("context", "general")
546
- role = data.get("role", "")
547
-
548
- if context == "resume":
549
- enhanced = gemini_client.enhance_resume_description(text, role)
550
- elif context == "portfolio":
551
- project_data = {
552
- "title": role,
553
- "description": text,
554
- "technologies": data.get("technologies", []),
555
- "role": data.get("your_role", "Developer"),
556
- }
557
- enhanced = gemini_client.enhance_portfolio_description(project_data)
558
- else:
559
- enhanced = gemini_client.improve_text_quality(text, "professional")
560
-
561
- return jsonify({"original": text, "enhanced": enhanced, "success": True})
562
-
563
- except Exception as e:
564
- print(f"Error enhancing description: {str(e)}", file=sys.stderr)
565
- return jsonify({"error": str(e)}), 500
566
-
567
-
568
- @app.route("/enhance-skills-summary", methods=["POST"])
569
- def enhance_skills_summary():
570
- """
571
- Generate professional skills summary
572
- Expected JSON:
573
- {
574
- "skills": ["Python", "React", "AWS"],
575
- "experience_years": 5
576
- }
577
- """
578
- try:
579
- data = request.get_json()
580
-
581
- if not data or not data.get("skills"):
582
- return jsonify({"error": "Skills list is required"}), 400
583
-
584
- skills = data["skills"]
585
- years = data.get("experience_years", 0)
586
-
587
- summary = gemini_client.generate_skills_summary(skills, years)
588
-
589
- return jsonify({"skills": skills, "summary": summary, "success": True})
590
-
591
- except Exception as e:
592
- print(f"Error generating skills summary: {str(e)}", file=sys.stderr)
593
- return jsonify({"error": str(e)}), 500
594
-
595
-
596
- # ============================================================================
597
- # ERROR HANDLERS
598
- # ============================================================================
599
-
600
-
601
- @app.errorhandler(404)
602
- def not_found(error):
603
- """Handle 404 errors"""
604
- return jsonify({"error": "Endpoint not found"}), 404
605
-
606
-
607
- @app.errorhandler(500)
608
- def internal_error(error):
609
- """Handle 500 errors"""
610
- return jsonify({"error": "Internal server error"}), 500
611
-
612
-
613
- # ============================================================================
614
- # MAIN
615
- # ============================================================================
616
-
617
- if __name__ == "__main__":
618
- port = int(os.environ.get("PORT", 7860))
619
- app.run(host="0.0.0.0", port=port, debug=False)
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Vero Template Generator Backend
3
+ Flask API for generating professional documents using Gemini AI
4
+ """
5
+
6
+ import io
7
+ import os
8
+ import sys
9
+ from datetime import datetime
10
+
11
+ from flask import Flask, jsonify, request, send_file
12
+ from flask_cors import CORS
13
+ from utils.docx_generator import get_docx_generator
14
+ from utils.gemini_client import get_gemini_client
15
+ from utils.pdf_generator import get_pdf_generator
16
+
17
+ # Initialize Flask app
18
+ app = Flask(__name__)
19
+ CORS(app)
20
+
21
+ # Initialize services
22
+ try:
23
+ gemini_client = get_gemini_client()
24
+ docx_generator = get_docx_generator()
25
+ pdf_generator = get_pdf_generator()
26
+ print("✓ All services initialized successfully", file=sys.stderr)
27
+ except Exception as e:
28
+ print(f"✗ Error initializing services: {str(e)}", file=sys.stderr)
29
+ sys.exit(1)
30
+
31
+
32
+ # ============================================================================
33
+ # HEALTH CHECK
34
+ # ============================================================================
35
+
36
+
37
+ @app.route("/health", methods=["GET"])
38
+ def health_check():
39
+ """Health check endpoint"""
40
+ return jsonify(
41
+ {
42
+ "status": "healthy",
43
+ "service": "Vero Template Generator",
44
+ "version": "1.0.0",
45
+ "timestamp": datetime.now().isoformat(),
46
+ "endpoints": [
47
+ "/generate-resume",
48
+ "/generate-cover-letter",
49
+ "/generate-proposal",
50
+ "/generate-invoice",
51
+ "/generate-contract",
52
+ "/generate-portfolio-pdf",
53
+ "/enhance-description",
54
+ "/enhance-skills-summary",
55
+ ],
56
+ }
57
+ )
58
+
59
+
60
+ # ============================================================================
61
+ # RESUME GENERATOR
62
+ # ============================================================================
63
+
64
+
65
+ @app.route("/generate-resume", methods=["POST"])
66
+ def generate_resume():
67
+ """
68
+ Generate professional resume
69
+ Expected JSON:
70
+ {
71
+ "personal_info": {
72
+ "name": "John Doe",
73
+ "email": "john@example.com",
74
+ "phone": "+1234567890",
75
+ "location": "City, State",
76
+ "linkedin": "linkedin.com/in/johndoe",
77
+ "website": "johndoe.com"
78
+ },
79
+ "summary": "Professional summary text",
80
+ "experience": [
81
+ {
82
+ "title": "Senior Developer",
83
+ "company": "Tech Corp",
84
+ "location": "City, State",
85
+ "start_date": "Jan 2020",
86
+ "end_date": "Present",
87
+ "responsibilities": ["Task 1", "Task 2"]
88
+ }
89
+ ],
90
+ "education": [
91
+ {
92
+ "degree": "Bachelor of Science",
93
+ "field": "Computer Science",
94
+ "school": "University Name",
95
+ "graduation_date": "May 2019",
96
+ "gpa": "3.8",
97
+ "honors": "Magna Cum Laude"
98
+ }
99
+ ],
100
+ "skills": ["Python", "JavaScript", "React"],
101
+ "certifications": [
102
+ {
103
+ "name": "AWS Certified",
104
+ "issuer": "Amazon",
105
+ "date": "2023"
106
+ }
107
+ ],
108
+ "projects": [
109
+ {
110
+ "name": "Project Name",
111
+ "description": "Description",
112
+ "technologies": ["Tech1", "Tech2"]
113
+ }
114
+ ],
115
+ "enhance_with_ai": true
116
+ }
117
+ """
118
+ try:
119
+ data = request.get_json()
120
+
121
+ if not data:
122
+ return jsonify({"error": "No data provided"}), 400
123
+
124
+ # Optional: Enhance descriptions with AI
125
+ if data.get("enhance_with_ai", False):
126
+ print("Enhancing resume content with AI...", file=sys.stderr)
127
+
128
+ # Enhance professional summary
129
+ if data.get("summary"):
130
+ skills = data.get("skills", [])
131
+ if isinstance(skills, dict):
132
+ skills = [s for skill_list in skills.values() for s in skill_list]
133
+ data["summary"] = gemini_client.generate_skills_summary(
134
+ skills, data.get("years_experience", 0)
135
+ )
136
+
137
+ # Enhance work experience descriptions
138
+ if data.get("experience"):
139
+ for exp in data["experience"]:
140
+ if exp.get("responsibilities"):
141
+ enhanced_resps = []
142
+ for resp in exp["responsibilities"]:
143
+ try:
144
+ enhanced = gemini_client.enhance_resume_description(
145
+ resp, exp.get("title", "")
146
+ )
147
+ enhanced_resps.append(enhanced)
148
+ except Exception as e:
149
+ print(
150
+ f"Error enhancing responsibility: {str(e)}",
151
+ file=sys.stderr,
152
+ )
153
+ enhanced_resps.append(resp)
154
+ exp["responsibilities"] = enhanced_resps
155
+
156
+ # Enhance project descriptions
157
+ if data.get("projects"):
158
+ for proj in data["projects"]:
159
+ if proj.get("description"):
160
+ try:
161
+ enhanced = gemini_client.enhance_portfolio_description(proj)
162
+ proj["description"] = enhanced
163
+ except Exception as e:
164
+ print(f"Error enhancing project: {str(e)}", file=sys.stderr)
165
+
166
+ # Generate DOCX
167
+ print("Generating resume document...", file=sys.stderr)
168
+ docx_buffer = docx_generator.generate_resume(data)
169
+
170
+ # Prepare filename
171
+ name = data.get("personal_info", {}).get("name", "Resume")
172
+ filename = f"{name.replace(' ', '_')}_Resume.docx"
173
+
174
+ return send_file(
175
+ docx_buffer,
176
+ mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
177
+ as_attachment=True,
178
+ download_name=filename,
179
+ )
180
+
181
+ except Exception as e:
182
+ print(f"Error generating resume: {str(e)}", file=sys.stderr)
183
+ return jsonify({"error": str(e)}), 500
184
+
185
+
186
+ # ============================================================================
187
+ # COVER LETTER GENERATOR
188
+ # ============================================================================
189
+
190
+
191
+ @app.route("/generate-cover-letter", methods=["POST"])
192
+ def generate_cover_letter():
193
+ """
194
+ Generate personalized cover letter
195
+ Expected JSON:
196
+ {
197
+ "name": "John Doe",
198
+ "address": "123 Main St, City, State",
199
+ "email": "john@example.com",
200
+ "phone": "+1234567890",
201
+ "date": "January 15, 2024",
202
+ "company": "Tech Corp",
203
+ "hiring_manager": "Jane Smith",
204
+ "position": "Senior Developer",
205
+ "skills": ["Python", "React", "AWS"],
206
+ "experience": "5 years of full-stack development",
207
+ "tone": "formal",
208
+ "custom_content": "Optional pre-written content",
209
+ "generate_with_ai": true
210
+ }
211
+ """
212
+ try:
213
+ data = request.get_json()
214
+
215
+ if not data:
216
+ return jsonify({"error": "No data provided"}), 400
217
+
218
+ # Generate content with AI if requested
219
+ if data.get("generate_with_ai", True) and not data.get("custom_content"):
220
+ print("Generating cover letter content with AI...", file=sys.stderr)
221
+ content = gemini_client.generate_cover_letter(data)
222
+ data["content"] = content
223
+ elif data.get("custom_content"):
224
+ data["content"] = data["custom_content"]
225
+ else:
226
+ return (
227
+ jsonify(
228
+ {
229
+ "error": "Either generate_with_ai must be true or custom_content must be provided"
230
+ }
231
+ ),
232
+ 400,
233
+ )
234
+
235
+ # Generate DOCX
236
+ print("Generating cover letter document...", file=sys.stderr)
237
+ docx_buffer = docx_generator.generate_cover_letter(data)
238
+
239
+ # Prepare filename
240
+ name = data.get("name", "Applicant").replace(" ", "_")
241
+ company = data.get("company", "Company").replace(" ", "_")
242
+ filename = f"{name}_CoverLetter_{company}.docx"
243
+
244
+ return send_file(
245
+ docx_buffer,
246
+ mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
247
+ as_attachment=True,
248
+ download_name=filename,
249
+ )
250
+
251
+ except Exception as e:
252
+ print(f"Error generating cover letter: {str(e)}", file=sys.stderr)
253
+ return jsonify({"error": str(e)}), 500
254
+
255
+
256
+ # ============================================================================
257
+ # PROPOSAL GENERATOR
258
+ # ============================================================================
259
+
260
+
261
+ @app.route("/generate-proposal", methods=["POST"])
262
+ def generate_proposal():
263
+ """
264
+ Generate business proposal
265
+ Expected JSON:
266
+ {
267
+ "title": "Web Development Proposal",
268
+ "client_name": "ABC Company",
269
+ "prepared_by": "Your Company Name",
270
+ "date": "January 15, 2024",
271
+ "project_title": "E-commerce Website",
272
+ "scope": "Develop a full-featured e-commerce platform",
273
+ "deliverables": ["Website", "Admin Panel", "Mobile App"],
274
+ "timeline": "3 months",
275
+ "budget": "$50,000",
276
+ "generate_with_ai": true,
277
+ "custom_content": "Optional pre-written proposal"
278
+ }
279
+ """
280
+ try:
281
+ data = request.get_json()
282
+
283
+ if not data:
284
+ return jsonify({"error": "No data provided"}), 400
285
+
286
+ # Generate proposal content with AI if requested
287
+ if data.get("generate_with_ai", True) and not data.get("custom_content"):
288
+ print("Generating proposal content with AI...", file=sys.stderr)
289
+ content = gemini_client.generate_proposal(data)
290
+ data["content"] = content
291
+ elif data.get("custom_content"):
292
+ data["content"] = data["custom_content"]
293
+
294
+ # Generate DOCX
295
+ print("Generating proposal document...", file=sys.stderr)
296
+ docx_buffer = docx_generator.generate_proposal(data)
297
+
298
+ # Prepare filename
299
+ client = data.get("client_name", "Client").replace(" ", "_")
300
+ title = data.get("project_title", "Proposal").replace(" ", "_")
301
+ filename = f"Proposal_{client}_{title}.docx"
302
+
303
+ return send_file(
304
+ docx_buffer,
305
+ mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
306
+ as_attachment=True,
307
+ download_name=filename,
308
+ )
309
+
310
+ except Exception as e:
311
+ print(f"Error generating proposal: {str(e)}", file=sys.stderr)
312
+ return jsonify({"error": str(e)}), 500
313
+
314
+
315
+ # ============================================================================
316
+ # INVOICE GENERATOR
317
+ # ============================================================================
318
+
319
+
320
+ @app.route("/generate-invoice", methods=["POST"])
321
+ def generate_invoice():
322
+ """
323
+ Generate professional invoice
324
+ Expected JSON:
325
+ {
326
+ "invoice_number": "INV-001",
327
+ "invoice_date": "2024-01-15",
328
+ "due_date": "2024-02-15",
329
+ "from_info": {
330
+ "name": "Your Business",
331
+ "address": "123 Business St",
332
+ "email": "billing@business.com",
333
+ "phone": "+1234567890"
334
+ },
335
+ "to_info": {
336
+ "name": "Client Name",
337
+ "address": "456 Client Ave",
338
+ "email": "client@example.com"
339
+ },
340
+ "items": [
341
+ {
342
+ "description": "Service/Product",
343
+ "quantity": 1,
344
+ "rate": 1000,
345
+ "amount": 1000
346
+ }
347
+ ],
348
+ "tax_rate": 8.5,
349
+ "discount": 0,
350
+ "notes": "Thank you for your business",
351
+ "payment_instructions": "Payment via bank transfer"
352
+ }
353
+ """
354
+ try:
355
+ data = request.get_json()
356
+
357
+ if not data:
358
+ return jsonify({"error": "No data provided"}), 400
359
+
360
+ # Validate required fields
361
+ if not data.get("items"):
362
+ return jsonify({"error": "Invoice items are required"}), 400
363
+
364
+ # Generate DOCX
365
+ print("Generating invoice document...", file=sys.stderr)
366
+ docx_buffer = docx_generator.generate_invoice(data)
367
+
368
+ # Prepare filename
369
+ invoice_num = data.get("invoice_number", "INV-001").replace("/", "-")
370
+ filename = f"Invoice_{invoice_num}.docx"
371
+
372
+ return send_file(
373
+ docx_buffer,
374
+ mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
375
+ as_attachment=True,
376
+ download_name=filename,
377
+ )
378
+
379
+ except Exception as e:
380
+ print(f"Error generating invoice: {str(e)}", file=sys.stderr)
381
+ return jsonify({"error": str(e)}), 500
382
+
383
+
384
+ # ============================================================================
385
+ # CONTRACT GENERATOR
386
+ # ============================================================================
387
+
388
+
389
+ @app.route("/generate-contract", methods=["POST"])
390
+ def generate_contract():
391
+ """
392
+ Generate legal contract
393
+ Expected JSON:
394
+ {
395
+ "contract_type": "Freelance Service Agreement",
396
+ "date": "January 15, 2024",
397
+ "party1": {
398
+ "name": "Service Provider",
399
+ "address": "123 Provider St"
400
+ },
401
+ "party2": {
402
+ "name": "Client Name",
403
+ "address": "456 Client Ave"
404
+ },
405
+ "effective_date": "January 20, 2024",
406
+ "expiration_date": "December 31, 2024",
407
+ "custom_terms": "Net 30 payment terms",
408
+ "generate_with_ai": true,
409
+ "custom_content": "Optional pre-written terms"
410
+ }
411
+ """
412
+ try:
413
+ data = request.get_json()
414
+
415
+ if not data:
416
+ return jsonify({"error": "No data provided"}), 400
417
+
418
+ # Generate contract terms with AI if requested
419
+ if data.get("generate_with_ai", True) and not data.get("custom_content"):
420
+ print("Generating contract terms with AI...", file=sys.stderr)
421
+ contract_type = data.get("contract_type", "Service Agreement")
422
+ custom_terms = data.get("custom_terms", "")
423
+ terms = gemini_client.enhance_contract_terms(contract_type, custom_terms)
424
+ data["terms"] = terms
425
+ elif data.get("custom_content"):
426
+ data["terms"] = data["custom_content"]
427
+
428
+ # Generate DOCX
429
+ print("Generating contract document...", file=sys.stderr)
430
+ docx_buffer = docx_generator.generate_contract(data)
431
+
432
+ # Prepare filename
433
+ contract_type = data.get("contract_type", "Contract").replace(" ", "_")
434
+ filename = f"{contract_type}_Contract.docx"
435
+
436
+ return send_file(
437
+ docx_buffer,
438
+ mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
439
+ as_attachment=True,
440
+ download_name=filename,
441
+ )
442
+
443
+ except Exception as e:
444
+ print(f"Error generating contract: {str(e)}", file=sys.stderr)
445
+ return jsonify({"error": str(e)}), 500
446
+
447
+
448
+ # ============================================================================
449
+ # PORTFOLIO PDF EXPORT
450
+ # ============================================================================
451
+
452
+
453
+ @app.route("/generate-portfolio-pdf", methods=["POST"])
454
+ def generate_portfolio_pdf():
455
+ """
456
+ Generate portfolio PDF
457
+ Expected JSON:
458
+ {
459
+ "name": "John Doe",
460
+ "title": "Full Stack Developer",
461
+ "bio": "Professional bio text",
462
+ "contact": {
463
+ "email": "john@example.com",
464
+ "phone": "+1234567890",
465
+ "website": "johndoe.com",
466
+ "linkedin": "linkedin.com/in/johndoe"
467
+ },
468
+ "skills": ["Python", "React", "AWS"],
469
+ "experience": [...],
470
+ "education": [...],
471
+ "projects": [...],
472
+ "certifications": [...],
473
+ "enhance_with_ai": false
474
+ }
475
+ """
476
+ try:
477
+ data = request.get_json()
478
+
479
+ if not data:
480
+ return jsonify({"error": "No data provided"}), 400
481
+
482
+ # Optional AI enhancement
483
+ if data.get("enhance_with_ai", False):
484
+ print("Enhancing portfolio content with AI...", file=sys.stderr)
485
+
486
+ # Enhance bio
487
+ if data.get("bio"):
488
+ data["bio"] = gemini_client.improve_text_quality(
489
+ data["bio"], "professional"
490
+ )
491
+
492
+ # Enhance project descriptions
493
+ if data.get("projects"):
494
+ for proj in data["projects"]:
495
+ if proj.get("description"):
496
+ try:
497
+ enhanced = gemini_client.enhance_portfolio_description(proj)
498
+ proj["description"] = enhanced
499
+ except Exception as e:
500
+ print(f"Error enhancing project: {str(e)}", file=sys.stderr)
501
+
502
+ # Generate PDF
503
+ print("Generating portfolio PDF...", file=sys.stderr)
504
+ pdf_buffer = pdf_generator.generate_portfolio_pdf(data)
505
+
506
+ # Prepare filename
507
+ name = data.get("name", "Portfolio").replace(" ", "_")
508
+ filename = f"{name}_Portfolio.pdf"
509
+
510
+ return send_file(
511
+ pdf_buffer,
512
+ mimetype="application/pdf",
513
+ as_attachment=True,
514
+ download_name=filename,
515
+ )
516
+
517
+ except Exception as e:
518
+ print(f"Error generating portfolio PDF: {str(e)}", file=sys.stderr)
519
+ return jsonify({"error": str(e)}), 500
520
+
521
+
522
+ # ============================================================================
523
+ # AI ENHANCEMENT UTILITIES
524
+ # ============================================================================
525
+
526
+
527
+ @app.route("/enhance-description", methods=["POST"])
528
+ def enhance_description():
529
+ """
530
+ Enhance text description with AI
531
+ Expected JSON:
532
+ {
533
+ "text": "Original text to enhance",
534
+ "context": "resume/portfolio/proposal",
535
+ "role": "Optional job role for context"
536
+ }
537
+ """
538
+ try:
539
+ data = request.get_json()
540
+
541
+ if not data or not data.get("text"):
542
+ return jsonify({"error": "Text is required"}), 400
543
+
544
+ text = data["text"]
545
+ context = data.get("context", "general")
546
+ role = data.get("role", "")
547
+
548
+ if context == "resume":
549
+ enhanced = gemini_client.enhance_resume_description(text, role)
550
+ elif context == "portfolio":
551
+ project_data = {
552
+ "title": role,
553
+ "description": text,
554
+ "technologies": data.get("technologies", []),
555
+ "role": data.get("your_role", "Developer"),
556
+ }
557
+ enhanced = gemini_client.enhance_portfolio_description(project_data)
558
+ else:
559
+ enhanced = gemini_client.improve_text_quality(text, "professional")
560
+
561
+ return jsonify({"original": text, "enhanced": enhanced, "success": True})
562
+
563
+ except Exception as e:
564
+ print(f"Error enhancing description: {str(e)}", file=sys.stderr)
565
+ return jsonify({"error": str(e)}), 500
566
+
567
+
568
+ @app.route("/enhance-skills-summary", methods=["POST"])
569
+ def enhance_skills_summary():
570
+ """
571
+ Generate professional skills summary
572
+ Expected JSON:
573
+ {
574
+ "skills": ["Python", "React", "AWS"],
575
+ "experience_years": 5
576
+ }
577
+ """
578
+ try:
579
+ data = request.get_json()
580
+
581
+ if not data or not data.get("skills"):
582
+ return jsonify({"error": "Skills list is required"}), 400
583
+
584
+ skills = data["skills"]
585
+ years = data.get("experience_years", 0)
586
+
587
+ summary = gemini_client.generate_skills_summary(skills, years)
588
+
589
+ return jsonify({"skills": skills, "summary": summary, "success": True})
590
+
591
+ except Exception as e:
592
+ print(f"Error generating skills summary: {str(e)}", file=sys.stderr)
593
+ return jsonify({"error": str(e)}), 500
594
+
595
+ @app.route("/", methods=["GET"])
596
+ def index():
597
+ return jsonify({
598
+ "service": "Vero Template Generator",
599
+ "message": "API is running",
600
+ "health": "/health"
601
+ })
602
+
603
+
604
+ # ============================================================================
605
+ # ERROR HANDLERS
606
+ # ============================================================================
607
+
608
+
609
+ @app.errorhandler(404)
610
+ def not_found(error):
611
+ """Handle 404 errors"""
612
+ return jsonify({"error": "Endpoint not found"}), 404
613
+
614
+
615
+ @app.errorhandler(500)
616
+ def internal_error(error):
617
+ """Handle 500 errors"""
618
+ return jsonify({"error": "Internal server error"}), 500
619
+
620
+
621
+ # ============================================================================
622
+ # MAIN
623
+ # ============================================================================
624
+
625
+ if __name__ == "__main__":
626
+ port = int(os.environ.get("PORT", 7860))
627
+ app.run(host="0.0.0.0", port=port, debug=False)