Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| from datetime import datetime | |
| from utils import generate_cover_letter | |
| import os | |
| # Custom CSS for modern UI | |
| css = """ | |
| :root { | |
| --primary: #4361ee; | |
| --secondary: #3f37c9; | |
| --accent: #4895ef; | |
| --light: #f8f9fa; | |
| --dark: #212529; | |
| } | |
| body { | |
| font-family: 'Segoe UI', system-ui, sans-serif; | |
| background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%); | |
| min-height: 100vh; | |
| margin: 0; | |
| padding: 20px; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| } | |
| .header { | |
| background: linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)), url('file/assets/header.jpg'); | |
| background-size: cover; | |
| color: white; | |
| padding: 2rem; | |
| border-radius: 12px; | |
| text-align: center; | |
| margin-bottom: 2rem; | |
| } | |
| .step-card { | |
| background: white; | |
| border-radius: 12px; | |
| box-shadow: 0 4px 20px rgba(0,0,0,0.08); | |
| padding: 1.5rem; | |
| margin-bottom: 1.5rem; | |
| } | |
| .step-indicator { | |
| display: flex; | |
| justify-content: center; | |
| margin-bottom: 2rem; | |
| } | |
| .step { | |
| width: 36px; | |
| height: 36px; | |
| border-radius: 50%; | |
| background: var(--accent); | |
| color: white; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-weight: bold; | |
| margin: 0 10px; | |
| position: relative; | |
| } | |
| .step.active { | |
| background: var(--secondary); | |
| transform: scale(1.1); | |
| } | |
| .step:not(:last-child)::after { | |
| content: ''; | |
| position: absolute; | |
| width: 40px; | |
| height: 2px; | |
| background: #ddd; | |
| left: 100%; | |
| } | |
| h2 { | |
| color: var(--primary); | |
| border-bottom: 2px solid var(--accent); | |
| padding-bottom: 0.5rem; | |
| margin-top: 0; | |
| } | |
| .btn-primary { | |
| background: var(--primary) !important; | |
| border: none !important; | |
| padding: 12px 24px !important; | |
| font-size: 16px !important; | |
| border-radius: 8px !important; | |
| transition: all 0.3s !important; | |
| } | |
| .btn-primary:hover { | |
| background: var(--secondary) !important; | |
| transform: translateY(-2px); | |
| box-shadow: 0 4px 12px rgba(67, 97, 238, 0.3) !important; | |
| } | |
| .output-card { | |
| background: white; | |
| border-radius: 12px; | |
| box-shadow: 0 4px 20px rgba(0,0,0,0.08); | |
| padding: 2rem; | |
| margin-top: 2rem; | |
| } | |
| .tips { | |
| background: #e3f2fd; | |
| border-left: 4px solid var(--accent); | |
| padding: 1rem; | |
| border-radius: 4px; | |
| font-size: 0.9rem; | |
| } | |
| .footer { | |
| text-align: center; | |
| margin-top: 2rem; | |
| color: #6c757d; | |
| font-size: 0.9rem; | |
| } | |
| """ | |
| # Initialize steps | |
| current_step = gr.State(1) | |
| def next_step(): | |
| return current_step.value + 1 | |
| def generate_letter(job_desc, resume, name, email, phone, company, manager, tone): | |
| # Generate cover letter body using NLP | |
| letter_body = generate_cover_letter(job_desc, resume, tone) | |
| # Format final letter | |
| today = datetime.today().strftime("%B %d, %Y") | |
| return f""" | |
| {today} | |
| {manager or "Hiring Manager"} | |
| {company or "Company Name"} | |
| Dear {manager.split()[0] + ',' if manager else "Hiring Manager,"} | |
| {letter_body} | |
| Sincerely, | |
| {name} | |
| {email} | {phone} | |
| """, current_step.value + 1 | |
| def update_step(step): | |
| return step | |
| with gr.Blocks(css=css, title="AI Cover Letter Generator") as demo: | |
| current_step = gr.Variable(1) | |
| with gr.Column(elem_classes=["container"]): | |
| # Header section | |
| with gr.Column(elem_classes=["header"]): | |
| gr.Markdown("# βοΈ AI Cover Letter Generator") | |
| gr.Markdown("Create personalized cover letters in seconds") | |
| # Step indicator | |
| with gr.Column(elem_classes=["step-indicator"]): | |
| gr.Markdown("### Create in 3 Steps") | |
| with gr.Row(): | |
| step1 = gr.Markdown("1", elem_classes=["step", "active"]) | |
| step2 = gr.Markdown("2", elem_classes=["step"]) | |
| step3 = gr.Markdown("3", elem_classes=["step"]) | |
| # Step 1: Personal Details | |
| with gr.Column(visible=True) as step1_section: | |
| with gr.Column(elem_classes=["step-card"]): | |
| gr.Markdown("### π€ Your Information") | |
| with gr.Row(): | |
| name = gr.Textbox(label="Full Name", placeholder="John Doe") | |
| email = gr.Textbox(label="Email", placeholder="john@example.com") | |
| with gr.Row(): | |
| phone = gr.Textbox(label="Phone", placeholder="(123) 456-7890") | |
| tone = gr.Dropdown( | |
| label="Writing Tone", | |
| choices=["Professional", "Enthusiastic", "Formal", "Friendly"], | |
| value="Professional" | |
| ) | |
| # Step 2: Job & Resume Details | |
| with gr.Column(visible=False) as step2_section: | |
| with gr.Column(elem_classes=["step-card"]): | |
| gr.Markdown("### πΌ Job & Company Details") | |
| with gr.Row(): | |
| company = gr.Textbox(label="Company Name", placeholder="Acme Inc") | |
| manager = gr.Textbox(label="Hiring Manager (Optional)", placeholder="Sarah Johnson") | |
| job_desc = gr.Textbox( | |
| label="Job Description", | |
| lines=5, | |
| placeholder="Paste the job description here...", | |
| info="Copy-paste the full job posting for best results" | |
| ) | |
| gr.Markdown("### π Your Resume") | |
| resume = gr.Textbox( | |
| label="Paste your resume content", | |
| lines=8, | |
| placeholder="Skills:\n- Python\n- Project Management\n\nExperience:\n- ABC Corp (2020-2023)...", | |
| info="Include key skills and experiences" | |
| ) | |
| with gr.Accordion("π‘ Resume Tips", open=False): | |
| gr.Markdown("- Include specific skills mentioned in the job description\n- Add quantifiable achievements\n- Keep it concise (300-500 words)") | |
| # Step 3: Generated Letter | |
| with gr.Column(visible=False) as step3_section: | |
| with gr.Column(elem_classes=["output-card"]): | |
| gr.Markdown("### π Your Custom Cover Letter") | |
| output = gr.Textbox(label="", lines=15, interactive=True) | |
| with gr.Row(): | |
| download = gr.Button("π₯ Download as TXT") | |
| new_letter = gr.Button("π Generate New Letter") | |
| with gr.Column(elem_classes=["tips"]): | |
| gr.Markdown("**Before using:**\n1. Review for accuracy\n2. Personalize company-specific details\n3. Check length (200-300 words)") | |
| # Navigation buttons | |
| with gr.Row(): | |
| back_btn = gr.Button("β Back", visible=False) | |
| next_btn = gr.Button("Continue β", elem_classes=["btn-primary"]) | |
| submit_btn = gr.Button("β¨ Generate Cover Letter", visible=False, elem_classes=["btn-primary"]) | |
| # Footer | |
| gr.Markdown("---") | |
| with gr.Column(elem_classes=["footer"]): | |
| gr.Markdown("β οΈ Always review generated content before sending") | |
| gr.Markdown("Made with β€οΈ using Transformers | GPT-2 Model") | |
| # Step navigation logic | |
| next_btn.click( | |
| next_step, | |
| inputs=None, | |
| outputs=current_step | |
| ).then( | |
| update_step, | |
| inputs=current_step, | |
| outputs=[step1, step2, step3] | |
| ).then( | |
| lambda step: { | |
| step1_section: gr.update(visible=step == 1), | |
| step2_section: gr.update(visible=step == 2), | |
| step3_section: gr.update(visible=step == 3), | |
| back_btn: gr.update(visible=step > 1), | |
| next_btn: gr.update(visible=step < 2), | |
| submit_btn: gr.update(visible=step == 2) | |
| }, | |
| inputs=current_step, | |
| outputs=[step1_section, step2_section, step3_section, back_btn, next_btn, submit_btn] | |
| ) | |
| back_btn.click( | |
| lambda step: step - 1, | |
| inputs=current_step, | |
| outputs=current_step | |
| ).then( | |
| update_step, | |
| inputs=current_step, | |
| outputs=[step1, step2, step3] | |
| ).then( | |
| lambda step: { | |
| step1_section: gr.update(visible=step == 1), | |
| step2_section: gr.update(visible=step == 2), | |
| step3_section: gr.update(visible=step == 3), | |
| back_btn: gr.update(visible=step > 1), | |
| next_btn: gr.update(visible=step < 2), | |
| submit_btn: gr.update(visible=step == 2) | |
| }, | |
| inputs=current_step, | |
| outputs=[step1_section, step2_section, step3_section, back_btn, next_btn, submit_btn] | |
| ) | |
| submit_btn.click( | |
| generate_letter, | |
| inputs=[job_desc, resume, name, email, phone, company, manager, tone], | |
| outputs=[output, current_step] | |
| ).then( | |
| update_step, | |
| inputs=current_step, | |
| outputs=[step1, step2, step3] | |
| ).then( | |
| lambda step: { | |
| step1_section: gr.update(visible=step == 1), | |
| step2_section: gr.update(visible=step == 2), | |
| step3_section: gr.update(visible=step == 3), | |
| back_btn: gr.update(visible=step > 1), | |
| next_btn: gr.update(visible=step < 2), | |
| submit_btn: gr.update(visible=step == 2) | |
| }, | |
| inputs=current_step, | |
| outputs=[step1_section, step2_section, step3_section, back_btn, next_btn, submit_btn] | |
| ) | |
| download.click( | |
| lambda text: { | |
| "name": "cover_letter.txt", | |
| "data": text | |
| }, | |
| inputs=output, | |
| outputs=gr.File() | |
| ) | |
| new_letter.click( | |
| lambda: 1, | |
| inputs=None, | |
| outputs=current_step | |
| ).then( | |
| update_step, | |
| inputs=current_step, | |
| outputs=[step1, step2, step3] | |
| ).then( | |
| lambda step: { | |
| step1_section: gr.update(visible=step == 1), | |
| step2_section: gr.update(visible=step == 2), | |
| step3_section: gr.update(visible=step == 3), | |
| back_btn: gr.update(visible=step > 1), | |
| next_btn: gr.update(visible=step < 2), | |
| submit_btn: gr.update(visible=step == 2) | |
| }, | |
| inputs=current_step, | |
| outputs=[step1_section, step2_section, step3_section, back_btn, next_btn, submit_btn] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() |