SMART_AI_RESUME / utils /resume_builder.py
parthib07's picture
Upload 531 files
d7d3dff verified
from docx import Document
from docx.shared import Pt, Inches, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.section import WD_SECTION
from docx.enum.style import WD_STYLE_TYPE
from docx.oxml import parse_xml
from docx.oxml.ns import nsdecls
from io import BytesIO
import tempfile
import traceback
class ResumeBuilder:
def __init__(self):
self.templates = {
"Modern": self.build_modern_template,
"Professional": self.build_professional_template,
"Minimal": self.build_minimal_template,
"Creative": self.build_creative_template
}
def generate_resume(self, data):
"""Generate a resume based on the provided data and template"""
try:
print(f"Starting resume generation with template: {data['template']}")
# Create a new document
doc = Document()
# Select and apply template
template_name = data['template'].lower()
print(f"Using template: {template_name}")
if template_name == 'modern':
doc = self.build_modern_template(doc, data)
elif template_name == 'professional':
doc = self.build_professional_template(doc, data)
elif template_name == 'minimal':
doc = self.build_minimal_template(doc, data)
elif template_name == 'creative':
doc = self.build_creative_template(doc, data)
else:
print(f"Warning: Unknown template '{template_name}', falling back to modern template")
doc = self.build_modern_template(doc, data)
# Save to buffer
buffer = BytesIO()
print("Saving document to buffer...")
doc.save(buffer)
buffer.seek(0)
print("Resume generated successfully!")
return buffer
except Exception as e:
print(f"Error in generate_resume: {str(e)}")
print(f"Full traceback: {traceback.format_exc()}")
print(f"Template data: {data}")
raise
def _format_list_items(self, items):
"""Helper function to handle both string and list inputs"""
if isinstance(items, str):
return [item.strip() for item in items.split('\n') if item.strip()]
elif isinstance(items, list):
return [item.strip() for item in items if item and item.strip()]
return []
def build_modern_template(self, doc, data):
"""Build modern style resume with clean, minimalist design"""
try:
# Set up styles
styles = doc.styles
# Name style - Modern, clean look
name_style = styles.add_style('Modern Name', WD_STYLE_TYPE.PARAGRAPH) if 'Modern Name' not in styles else styles['Modern Name']
name_style.font.size = Pt(24)
name_style.font.bold = True
name_style.font.color.rgb = RGBColor(41, 128, 185) # Modern blue
name_style.font.name = 'Arial'
name_style.paragraph_format.space_after = Pt(0)
name_style.paragraph_format.space_before = Pt(6)
name_style.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
# Section style - Clean and modern
section_style = styles.add_style('Modern Section', WD_STYLE_TYPE.PARAGRAPH) if 'Modern Section' not in styles else styles['Modern Section']
section_style.font.size = Pt(14)
section_style.font.bold = True
section_style.font.color.rgb = RGBColor(41, 128, 185) # Modern blue
section_style.font.name = 'Arial'
section_style.paragraph_format.space_before = Pt(16)
section_style.paragraph_format.space_after = Pt(4)
# Section underline style
section_underline = styles.add_style('Modern Section Underline', WD_STYLE_TYPE.PARAGRAPH) if 'Modern Section Underline' not in styles else styles['Modern Section Underline']
section_underline.font.size = Pt(8)
section_underline.font.color.rgb = RGBColor(41, 128, 185)
section_underline.paragraph_format.space_after = Pt(8)
# Normal text style
normal_style = styles.add_style('Modern Normal', WD_STYLE_TYPE.PARAGRAPH) if 'Modern Normal' not in styles else styles['Modern Normal']
normal_style.font.size = Pt(10)
normal_style.font.name = 'Arial'
normal_style.paragraph_format.space_after = Pt(2)
normal_style.font.color.rgb = RGBColor(44, 62, 80)
# Contact style
contact_style = styles.add_style('Modern Contact', WD_STYLE_TYPE.PARAGRAPH) if 'Modern Contact' not in styles else styles['Modern Contact']
contact_style.font.size = Pt(10)
contact_style.font.name = 'Arial'
contact_style.font.color.rgb = RGBColor(41, 128, 185)
contact_style.paragraph_format.space_after = Pt(2)
contact_style.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
# Add name at the top
name_paragraph = doc.add_paragraph(data['personal_info']['full_name'].upper())
name_paragraph.style = name_style
# Add role/title if available
if data['personal_info'].get('title'):
title = doc.add_paragraph(data['personal_info']['title'])
title.style = contact_style
# Contact information layout
contact_info = doc.add_paragraph()
contact_info.style = contact_style
# Add contact details with separators
contact_parts = []
if data['personal_info'].get('email'): contact_parts.append(data['personal_info']['email'])
if data['personal_info'].get('phone'): contact_parts.append(data['personal_info']['phone'])
if data['personal_info'].get('location'): contact_parts.append(data['personal_info']['location'])
if contact_parts:
contact_info.add_run(' | '.join(contact_parts))
# Links layout
if data['personal_info'].get('linkedin') or data['personal_info'].get('portfolio'):
links = doc.add_paragraph()
links.style = contact_style
links_parts = []
if data['personal_info'].get('linkedin'): links_parts.append(f"LinkedIn: {data['personal_info']['linkedin']}")
if data['personal_info'].get('portfolio'): links_parts.append(f"Portfolio: {data['personal_info']['portfolio']}")
links.add_run(' | '.join(links_parts))
# Professional Summary
if data.get('summary'):
doc.add_paragraph('PROFESSIONAL SUMMARY', style=section_style)
doc.add_paragraph('_' * 40, style=section_underline)
summary = doc.add_paragraph(data['summary'])
summary.style = normal_style
summary.paragraph_format.space_after = Pt(12)
summary.paragraph_format.left_indent = Inches(0.2)
# Experience Section
if data.get('experience'):
doc.add_paragraph('EXPERIENCE', style=section_style)
doc.add_paragraph('_' * 40, style=section_underline)
for exp in data['experience']:
p = doc.add_paragraph()
p.style = normal_style
p.paragraph_format.left_indent = Inches(0.2)
# Company and position
p.add_run(f"{exp['position']} at {exp['company']}").bold = True
date_run = p.add_run(f"\n{exp['start_date']} - {exp['end_date']}")
date_run.font.color.rgb = RGBColor(41, 128, 185)
if exp.get('description'):
desc = doc.add_paragraph(exp['description'])
desc.style = normal_style
desc.paragraph_format.left_indent = Inches(0.4)
if exp.get('responsibilities'):
for resp in self._format_list_items(exp['responsibilities']):
bullet = doc.add_paragraph()
bullet.style = normal_style
bullet.paragraph_format.left_indent = Inches(0.6)
bullet.add_run('β€’ ' + resp)
p.paragraph_format.space_after = Pt(12)
# Projects Section
if data.get('projects'):
doc.add_paragraph('PROJECTS', style=section_style)
doc.add_paragraph('_' * 40, style=section_underline)
for proj in data['projects']:
p = doc.add_paragraph()
p.style = normal_style
p.paragraph_format.left_indent = Inches(0.2)
p.add_run(proj['name']).bold = True
if proj.get('technologies'):
tech_run = p.add_run(f" | {proj['technologies']}")
tech_run.font.color.rgb = RGBColor(41, 128, 185)
if proj.get('description'):
desc = doc.add_paragraph(proj['description'])
desc.style = normal_style
desc.paragraph_format.left_indent = Inches(0.4)
if proj.get('responsibilities'):
for resp in self._format_list_items(proj['responsibilities']):
bullet = doc.add_paragraph()
bullet.style = normal_style
bullet.paragraph_format.left_indent = Inches(0.6)
bullet.add_run('β€’ ' + resp)
p.paragraph_format.space_after = Pt(12)
# Education Section
if data.get('education'):
doc.add_paragraph('EDUCATION', style=section_style)
doc.add_paragraph('_' * 40, style=section_underline)
for edu in data['education']:
p = doc.add_paragraph()
p.style = normal_style
p.paragraph_format.left_indent = Inches(0.2)
p.add_run(f"{edu['school']}").bold = True
p.add_run(f"\n{edu['degree']} in {edu['field']}")
date_run = p.add_run(f"\nGraduation: {edu['graduation_date']}")
if edu.get('gpa'):
p.add_run(f" | GPA: {edu['gpa']}")
p.paragraph_format.space_after = Pt(8)
# Skills Section
if data.get('skills'):
doc.add_paragraph('SKILLS', style=section_style)
doc.add_paragraph('_' * 40, style=section_underline)
skills = data['skills']
def add_skill_category(category_name, title):
if skills.get(category_name):
p = doc.add_paragraph()
p.style = normal_style
p.paragraph_format.left_indent = Inches(0.2)
p.add_run(f"{title}: ").bold = True
skills_text = ' β€’ '.join(self._format_list_items(skills[category_name]))
p.add_run(skills_text)
p.paragraph_format.space_after = Pt(6)
add_skill_category('technical', 'Technical Skills')
add_skill_category('soft', 'Soft Skills')
add_skill_category('languages', 'Languages')
add_skill_category('tools', 'Tools & Technologies')
# Set margins
sections = doc.sections
for section in sections:
section.top_margin = Inches(0.5)
section.bottom_margin = Inches(0.5)
section.left_margin = Inches(0.8)
section.right_margin = Inches(0.8)
return doc
except Exception as e:
print(f"Error in build_modern_template: {str(e)}")
raise
def build_professional_template(self, doc, data):
"""Build professional style resume with improved spacing and layout"""
try:
# Set up styles
styles = doc.styles
# Header style - Name
header_style = styles.add_style('Pro Header', WD_STYLE_TYPE.PARAGRAPH) if 'Pro Header' not in styles else styles['Pro Header']
header_style.font.size = Pt(24)
header_style.font.bold = True
header_style.font.color.rgb = RGBColor(0, 0, 0)
header_style.paragraph_format.space_after = Pt(4)
header_style.font.name = 'Calibri'
# Section style
section_style = styles.add_style('Pro Section', WD_STYLE_TYPE.PARAGRAPH) if 'Pro Section' not in styles else styles['Pro Section']
section_style.font.size = Pt(14)
section_style.font.bold = True
section_style.font.color.rgb = RGBColor(0, 120, 215)
section_style.paragraph_format.space_before = Pt(12)
section_style.paragraph_format.space_after = Pt(6)
section_style.font.name = 'Calibri'
# Normal text style
normal_style = styles.add_style('Pro Normal', WD_STYLE_TYPE.PARAGRAPH) if 'Pro Normal' not in styles else styles['Pro Normal']
normal_style.font.size = Pt(10)
normal_style.font.name = 'Calibri'
normal_style.paragraph_format.space_after = Pt(2)
# Contact style
contact_style = styles.add_style('Pro Contact', WD_STYLE_TYPE.PARAGRAPH) if 'Pro Contact' not in styles else styles['Pro Contact']
contact_style.font.size = Pt(10)
contact_style.font.name = 'Calibri'
contact_style.paragraph_format.space_after = Pt(6)
# Add name at the top
name_paragraph = doc.add_paragraph(data['personal_info']['full_name'])
name_paragraph.style = header_style
name_paragraph.alignment = WD_ALIGN_PARAGRAPH.LEFT
# Add contact information in a single line
contact_parts = []
if data['personal_info'].get('email'): contact_parts.append(data['personal_info']['email'])
if data['personal_info'].get('phone'): contact_parts.append(data['personal_info']['phone'])
if data['personal_info'].get('location'): contact_parts.append(data['personal_info']['location'])
if contact_parts:
contact = doc.add_paragraph()
contact.style = contact_style
contact.add_run(' | '.join(contact_parts))
# Add LinkedIn and Portfolio links
links_parts = []
if data['personal_info'].get('linkedin'): links_parts.append(f"LinkedIn: {data['personal_info']['linkedin']}")
if data['personal_info'].get('portfolio'): links_parts.append(f"Portfolio: {data['personal_info']['portfolio']}")
if links_parts:
links = doc.add_paragraph()
links.style = contact_style
links.add_run(' | '.join(links_parts))
# Professional Summary
if data.get('summary'):
doc.add_paragraph('PROFESSIONAL SUMMARY', style=section_style)
summary = doc.add_paragraph(data['summary'])
summary.style = normal_style
# Experience Section
if data.get('experience'):
doc.add_paragraph('EXPERIENCE', style=section_style)
for exp in data['experience']:
p = doc.add_paragraph()
p.style = normal_style
p.add_run(f"{exp['position']} at {exp['company']}").bold = True
p.add_run(f" | {exp['start_date']} - {exp['end_date']}")
if exp.get('description'):
desc = doc.add_paragraph(exp['description'])
desc.style = normal_style
desc.paragraph_format.left_indent = Inches(0.2)
if exp.get('responsibilities'):
for resp in self._format_list_items(exp['responsibilities']):
bullet = doc.add_paragraph()
bullet.style = normal_style
bullet.paragraph_format.left_indent = Inches(0.3)
bullet.add_run('β€’ ' + resp)
# Projects Section
if data.get('projects'):
doc.add_paragraph('PROJECTS', style=section_style)
for proj in data['projects']:
p = doc.add_paragraph()
p.style = normal_style
p.add_run(proj['name']).bold = True
if proj.get('technologies'):
p.add_run(f" | {proj['technologies']}")
if proj.get('description'):
desc = doc.add_paragraph(proj['description'])
desc.style = normal_style
desc.paragraph_format.left_indent = Inches(0.2)
if proj.get('responsibilities'):
for resp in self._format_list_items(proj['responsibilities']):
bullet = doc.add_paragraph()
bullet.style = normal_style
bullet.paragraph_format.left_indent = Inches(0.3)
bullet.add_run('β€’ ' + resp)
# Education Section
if data.get('education'):
doc.add_paragraph('EDUCATION', style=section_style)
for edu in data['education']:
p = doc.add_paragraph()
p.style = normal_style
p.add_run(f"{edu['school']}").bold = True
p.add_run(f"\n{edu['degree']} in {edu['field']}")
p.add_run(f" | Graduation: {edu['graduation_date']}")
if edu.get('gpa'):
p.add_run(f" | GPA: {edu['gpa']}")
# Skills Section
if data.get('skills'):
doc.add_paragraph('SKILLS', style=section_style)
skills = data['skills']
def add_skill_category(category_name, title):
if skills.get(category_name):
p = doc.add_paragraph()
p.style = normal_style
p.add_run(f"{title}: ").bold = True
skills_text = ', '.join(self._format_list_items(skills[category_name]))
p.add_run(skills_text)
add_skill_category('technical', 'Technical Skills')
add_skill_category('soft', 'Soft Skills')
add_skill_category('languages', 'Languages')
add_skill_category('tools', 'Tools & Technologies')
# Set margins for better space utilization
sections = doc.sections
for section in sections:
section.top_margin = Inches(0.5)
section.bottom_margin = Inches(0.5)
section.left_margin = Inches(0.7)
section.right_margin = Inches(0.7)
return doc
except Exception as e:
print(f"Error in build_professional_template: {str(e)}")
raise
def build_minimal_template(self, doc, data):
"""Build minimal style resume"""
try:
# Set up styles
styles = doc.styles
# Header style - Large, bold name
header_style = None
if 'Min Header' not in styles:
header_style = styles.add_style('Min Header', WD_STYLE_TYPE.PARAGRAPH)
header_style.font.size = Pt(28)
header_style.font.bold = True
header_style.font.color.rgb = RGBColor(33, 33, 33) # Dark gray
header_style.paragraph_format.space_after = Pt(4)
else:
header_style = styles['Min Header']
# Contact style - Small, gray text
contact_style = None
if 'Min Contact' not in styles:
contact_style = styles.add_style('Min Contact', WD_STYLE_TYPE.PARAGRAPH)
contact_style.font.size = Pt(9)
contact_style.font.color.rgb = RGBColor(100, 100, 100) # Light gray
contact_style.paragraph_format.space_after = Pt(12)
else:
contact_style = styles['Min Contact']
# Section style - Medium, all caps
section_style = None
if 'Min Section' not in styles:
section_style = styles.add_style('Min Section', WD_STYLE_TYPE.PARAGRAPH)
section_style.font.size = Pt(12)
section_style.font.all_caps = True
section_style.font.bold = True
section_style.font.color.rgb = RGBColor(33, 33, 33)
section_style.paragraph_format.space_before = Pt(16)
section_style.paragraph_format.space_after = Pt(8)
else:
section_style = styles['Min Section']
# Normal text style
normal_style = None
if 'Min Normal' not in styles:
normal_style = styles.add_style('Min Normal', WD_STYLE_TYPE.PARAGRAPH)
normal_style.font.size = Pt(10)
normal_style.font.color.rgb = RGBColor(33, 33, 33)
normal_style.paragraph_format.space_after = Pt(4)
else:
normal_style = styles['Min Normal']
# Add header with personal info
personal = data['personal_info']
name = doc.add_paragraph(personal['full_name'])
name.style = header_style
# Contact info in one line
contact_parts = []
if personal.get('email'): contact_parts.append(personal['email'])
if personal.get('phone'): contact_parts.append(personal['phone'])
if personal.get('location'): contact_parts.append(personal['location'])
if contact_parts:
contact = doc.add_paragraph()
contact.style = contact_style
contact.add_run(' β€’ '.join(contact_parts))
# Links in one line
links_parts = []
if personal.get('linkedin'): links_parts.append(f"LinkedIn: {personal['linkedin']}")
if personal.get('portfolio'): links_parts.append(f"Portfolio: {personal['portfolio']}")
if links_parts:
links = doc.add_paragraph()
links.style = contact_style
links.add_run(' β€’ '.join(links_parts))
# Professional Summary
if data.get('summary'):
doc.add_paragraph('SUMMARY', style=section_style)
summary = doc.add_paragraph(data['summary'])
summary.style = normal_style
# Experience Section
if data.get('experience'):
doc.add_paragraph('EXPERIENCE', style=section_style)
for exp in data['experience']:
p = doc.add_paragraph(style=normal_style)
p.add_run(f"{exp['position']} at {exp['company']}").bold = True
p.add_run(f"\n{exp['start_date']} - {exp['end_date']}")
if exp.get('description'): # Changed from 'overview' to 'description'
overview = doc.add_paragraph(exp['description'])
overview.style = normal_style
if exp.get('responsibilities'):
resp_para = doc.add_paragraph(style=normal_style)
resp_para.add_run('Key Responsibilities:').bold = True
for resp in self._format_list_items(exp['responsibilities']):
bullet = doc.add_paragraph(style=normal_style)
bullet.style.paragraph_format.left_indent = Inches(0.25)
bullet.add_run('β€’ ' + resp)
if exp.get('achievements'):
ach_para = doc.add_paragraph(style=normal_style)
ach_para.add_run('Key Achievements:').bold = True
for ach in self._format_list_items(exp['achievements']):
bullet = doc.add_paragraph(style=normal_style)
bullet.style.paragraph_format.left_indent = Inches(0.25)
bullet.add_run('β€’ ' + ach)
# Projects Section
if data.get('projects'):
doc.add_paragraph('PROJECTS', style=section_style)
for proj in data['projects']:
p = doc.add_paragraph(style=normal_style)
p.add_run(proj['name']).bold = True
if proj.get('technologies'):
p.add_run(f"\nTechnologies: {proj['technologies']}")
if proj.get('description'): # Changed from 'overview' to 'description'
overview = doc.add_paragraph(proj['description'])
overview.style = normal_style
if proj.get('responsibilities'):
resp_para = doc.add_paragraph(style=normal_style)
resp_para.add_run('Key Responsibilities:').bold = True
for resp in self._format_list_items(proj['responsibilities']):
bullet = doc.add_paragraph(style=normal_style)
bullet.style.paragraph_format.left_indent = Inches(0.25)
bullet.add_run('β€’ ' + resp)
if proj.get('achievements'):
ach_para = doc.add_paragraph(style=normal_style)
ach_para.add_run('Key Achievements:').bold = True
for ach in self._format_list_items(proj['achievements']):
bullet = doc.add_paragraph(style=normal_style)
bullet.style.paragraph_format.left_indent = Inches(0.25)
bullet.add_run('β€’ ' + ach)
if proj.get('link'):
link = doc.add_paragraph(f"Project Link: {proj['link']}")
link.style = normal_style
# Education Section
if data.get('education'):
doc.add_paragraph('EDUCATION', style=section_style)
for edu in data['education']:
p = doc.add_paragraph(style=normal_style)
p.add_run(f"{edu['school']} - {edu['degree']} in {edu['field']}").bold = True
p.add_run(f"\nGraduation: {edu['graduation_date']}")
if edu.get('gpa'):
p.add_run(f" | GPA: {edu['gpa']}")
if edu.get('achievements'):
ach_para = doc.add_paragraph(style=normal_style)
ach_para.add_run('Achievements & Activities:').bold = True
for ach in self._format_list_items(edu['achievements']):
bullet = doc.add_paragraph(style=normal_style)
bullet.style.paragraph_format.left_indent = Inches(0.25)
bullet.add_run('β€’ ' + ach)
# Skills Section
if data.get('skills'):
doc.add_paragraph('SKILLS', style=section_style)
skills = data['skills']
def add_skill_category(category_name, title):
if skills.get(category_name):
p = doc.add_paragraph(style=normal_style)
p.add_run(f"{title}: ").bold = True
p.add_run(' β€’ '.join(self._format_list_items(skills[category_name])))
add_skill_category('technical', 'Technical Skills')
add_skill_category('soft', 'Soft Skills')
add_skill_category('languages', 'Languages')
add_skill_category('tools', 'Tools & Technologies')
return doc
except Exception as e:
print(f"Error in build_minimal_template: {str(e)}")
raise
def build_creative_template(self, doc, data):
"""Build creative style resume with vibrant design and emojis"""
try:
# Set up styles
styles = doc.styles
# Name style - Creative and bold
name_style = styles.add_style('Creative Name', WD_STYLE_TYPE.PARAGRAPH) if 'Creative Name' not in styles else styles['Creative Name']
name_style.font.size = Pt(24)
name_style.font.bold = True
name_style.font.color.rgb = RGBColor(155, 89, 182) # Purple
name_style.font.name = 'Arial'
name_style.paragraph_format.space_after = Pt(4)
name_style.paragraph_format.space_before = Pt(6)
name_style.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
# Section style - Vibrant
section_style = styles.add_style('Creative Section', WD_STYLE_TYPE.PARAGRAPH) if 'Creative Section' not in styles else styles['Creative Section']
section_style.font.size = Pt(14)
section_style.font.bold = True
section_style.font.color.rgb = RGBColor(155, 89, 182) # Purple
section_style.font.name = 'Arial'
section_style.paragraph_format.space_before = Pt(16)
section_style.paragraph_format.space_after = Pt(4)
# Normal text style - Clean
normal_style = styles.add_style('Creative Normal', WD_STYLE_TYPE.PARAGRAPH) if 'Creative Normal' not in styles else styles['Creative Normal']
normal_style.font.size = Pt(10)
normal_style.font.name = 'Arial'
normal_style.paragraph_format.space_after = Pt(2)
normal_style.font.color.rgb = RGBColor(52, 73, 94) # Dark slate
# Contact style - Professional
contact_style = styles.add_style('Creative Contact', WD_STYLE_TYPE.PARAGRAPH) if 'Creative Contact' not in styles else styles['Creative Contact']
contact_style.font.size = Pt(10)
contact_style.font.name = 'Arial'
contact_style.font.color.rgb = RGBColor(155, 89, 182) # Purple
contact_style.paragraph_format.space_after = Pt(2)
contact_style.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
# Add name at the top
name_paragraph = doc.add_paragraph('✨ ' + data['personal_info']['full_name'] + ' ✨')
name_paragraph.style = name_style
# Add role/title if available
if data['personal_info'].get('title'):
title = doc.add_paragraph('πŸ’« ' + data['personal_info']['title'])
title.style = contact_style
# Contact information layout
contact_info = doc.add_paragraph()
contact_info.style = contact_style
contact_parts = []
if data['personal_info'].get('email'): contact_parts.append(f"πŸ“§ {data['personal_info']['email']}")
if data['personal_info'].get('phone'): contact_parts.append(f"πŸ“± {data['personal_info']['phone']}")
if data['personal_info'].get('location'): contact_parts.append(f"πŸ“ {data['personal_info']['location']}")
if contact_parts:
contact_info.add_run(' | '.join(contact_parts))
# Links with professional formatting
if data['personal_info'].get('linkedin') or data['personal_info'].get('portfolio'):
links = doc.add_paragraph()
links.style = contact_style
links_parts = []
if data['personal_info'].get('linkedin'): links_parts.append(f"πŸ”— LinkedIn: {data['personal_info']['linkedin']}")
if data['personal_info'].get('portfolio'): links_parts.append(f"🌐 Portfolio: {data['personal_info']['portfolio']}")
links.add_run(' | '.join(links_parts))
# Professional Summary
if data.get('summary'):
doc.add_paragraph('πŸ‘¨β€πŸ’Ό PROFESSIONAL SUMMARY', style=section_style)
summary = doc.add_paragraph(data['summary'])
summary.style = normal_style
summary.paragraph_format.space_after = Pt(12)
summary.paragraph_format.left_indent = Inches(0.2)
# Experience Section
if data.get('experience'):
doc.add_paragraph('πŸ’Ό EXPERIENCE', style=section_style)
for exp in data['experience']:
p = doc.add_paragraph()
p.style = normal_style
p.paragraph_format.left_indent = Inches(0.2)
p.add_run(f"πŸš€ {exp['position']}").bold = True
p.add_run(f"\n🏒 {exp['company']}")
p.add_run(f"\nπŸ“… {exp['start_date']} - {exp['end_date']}")
if exp.get('description'):
desc = doc.add_paragraph(exp['description'])
desc.style = normal_style
desc.paragraph_format.left_indent = Inches(0.4)
if exp.get('responsibilities'):
resp_para = doc.add_paragraph()
resp_para.style = normal_style
resp_para.paragraph_format.left_indent = Inches(0.4)
resp_para.add_run('🎯 Key Achievements:').bold = True
for resp in self._format_list_items(exp['responsibilities']):
bullet = doc.add_paragraph()
bullet.style = normal_style
bullet.paragraph_format.left_indent = Inches(0.6)
bullet.add_run('β€’ ' + resp)
p.paragraph_format.space_after = Pt(12)
# Projects Section
if data.get('projects'):
doc.add_paragraph('πŸ› οΈ PROJECTS', style=section_style)
for proj in data['projects']:
p = doc.add_paragraph()
p.style = normal_style
p.paragraph_format.left_indent = Inches(0.2)
p.add_run(f"✨ {proj['name']}").bold = True
if proj.get('technologies'):
p.add_run(f"\nπŸ’» Technologies: {proj['technologies']}")
if proj.get('description'):
desc = doc.add_paragraph(proj['description'])
desc.style = normal_style
desc.paragraph_format.left_indent = Inches(0.4)
if proj.get('responsibilities'):
resp_para = doc.add_paragraph()
resp_para.style = normal_style
resp_para.paragraph_format.left_indent = Inches(0.4)
resp_para.add_run('🎯 Key Features:').bold = True
for resp in self._format_list_items(proj['responsibilities']):
bullet = doc.add_paragraph()
bullet.style = normal_style
bullet.paragraph_format.left_indent = Inches(0.6)
bullet.add_run('β€’ ' + resp)
p.paragraph_format.space_after = Pt(12)
# Education Section
if data.get('education'):
doc.add_paragraph('πŸŽ“ EDUCATION', style=section_style)
for edu in data['education']:
p = doc.add_paragraph()
p.style = normal_style
p.paragraph_format.left_indent = Inches(0.2)
p.add_run(f"πŸ“š {edu['school']}").bold = True
p.add_run(f"\n🎯 {edu['degree']} in {edu['field']}")
p.add_run(f"\nπŸ“… Graduation: {edu['graduation_date']}")
if edu.get('gpa'):
p.add_run(f" | πŸ“Š GPA: {edu['gpa']}")
p.paragraph_format.space_after = Pt(8)
# Skills Section
if data.get('skills'):
doc.add_paragraph('⭐ SKILLS', style=section_style)
skills = data['skills']
def add_skill_category(category_name, title, icon):
if skills.get(category_name):
p = doc.add_paragraph()
p.style = normal_style
p.paragraph_format.left_indent = Inches(0.2)
p.add_run(f"{icon} {title}: ").bold = True
skills_text = ' β€’ '.join(self._format_list_items(skills[category_name]))
p.add_run(skills_text)
p.paragraph_format.space_after = Pt(6)
add_skill_category('technical', 'Technical Skills', 'πŸ’»')
add_skill_category('soft', 'Soft Skills', '🀝')
add_skill_category('languages', 'Languages', '🌐')
add_skill_category('tools', 'Tools & Technologies', 'πŸ› οΈ')
# Set margins
sections = doc.sections
for section in sections:
section.top_margin = Inches(0.5)
section.bottom_margin = Inches(0.5)
section.left_margin = Inches(0.8)
section.right_margin = Inches(0.8)
return doc
except Exception as e:
print(f"Error in build_creative_template: {str(e)}")
raise
def generate_preview(self, template_name, data):
"""Generate a live preview of the resume"""
if template_name not in self.preview_templates:
return None
template = self.preview_templates[template_name]
# Format skills as HTML
skills_html = ""
if 'skills' in data:
if template_name == 'Modern':
skills_html = "".join([f'<div class="skill">{skill}</div>' for skill in data['skills']])
else:
skills_html = "".join([f'<div class="skill-item">{skill}</div>' for skill in data['skills']])
# Format experience as HTML
experience_html = ""
if 'experience' in data:
for exp in data['experience']:
experience_html += f"""
<div class="experience-item">
<h3>{exp.get('title', '')}</h3>
<p class="company">{exp.get('company', '')}</p>
<p class="date">{exp.get('date', '')}</p>
<p class="description">{exp.get('description', '')}</p>
</div>
"""
# Format education as HTML
education_html = ""
if 'education' in data:
for edu in data['education']:
education_html += f"""
<div class="education-item">
<h3>{edu.get('degree', '')}</h3>
<p class="school">{edu.get('school', '')}</p>
<p class="date">{edu.get('date', '')}</p>
</div>
"""
# Combine HTML and CSS
preview_html = template['html'].format(
name=data.get('name', 'Your Name'),
email=data.get('email', 'email@example.com'),
phone=data.get('phone', '123-456-7890'),
linkedin=data.get('linkedin', 'linkedin.com/in/yourprofile'),
title=data.get('title', 'Your Title'),
summary=data.get('summary', 'Your professional summary...'),
experience=experience_html,
education=education_html,
skills=skills_html
)
return {
'html': preview_html,
'css': template['css']
}