Spaces:
No application file
No application file
| #!/usr/bin/env python3 | |
| import gradio as gr | |
| import os | |
| import re | |
| import tempfile | |
| from pathlib import Path | |
| # PDF processing imports | |
| try: | |
| import PyPDF2 | |
| import pdfplumber | |
| PDF_SUPPORT = True | |
| except ImportError: | |
| PDF_SUPPORT = False | |
| # LLM integration imports | |
| try: | |
| import openai | |
| LLM_SUPPORT = True | |
| except ImportError: | |
| LLM_SUPPORT = False | |
| # Basic configuration | |
| OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") | |
| ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY") | |
| SERPAPI_KEY = os.getenv("SERPAPI_KEY") | |
| FIRECRAWL_API_KEY = os.getenv("FIRECRAWL_API_KEY") | |
| def extract_text_from_pdf(pdf_file_path: str) -> str: | |
| """Extract text from PDF using multiple methods""" | |
| if not PDF_SUPPORT: | |
| return "β PDF support not available. Please install: pip install PyPDF2 pdfplumber" | |
| extracted_text = "" | |
| try: | |
| # Method 1: Try pdfplumber first (better for complex layouts) | |
| with pdfplumber.open(pdf_file_path) as pdf: | |
| text_parts = [] | |
| for page in pdf.pages: | |
| page_text = page.extract_text() | |
| if page_text: | |
| text_parts.append(page_text) | |
| extracted_text = "\n".join(text_parts) | |
| if extracted_text and len(extracted_text.strip()) > 50: | |
| return f"β PDF processed successfully with pdfplumber ({len(extracted_text)} characters)\n\n{extracted_text}" | |
| except Exception as e: | |
| print(f"Pdfplumber failed: {e}") | |
| try: | |
| # Method 2: Fallback to PyPDF2 | |
| with open(pdf_file_path, 'rb') as file: | |
| pdf_reader = PyPDF2.PdfReader(file) | |
| text_parts = [] | |
| for page in pdf_reader.pages: | |
| page_text = page.extract_text() | |
| if page_text: | |
| text_parts.append(page_text) | |
| extracted_text = "\n".join(text_parts) | |
| if extracted_text and len(extracted_text.strip()) > 50: | |
| return f"β PDF processed successfully with PyPDF2 ({len(extracted_text)} characters)\n\n{extracted_text}" | |
| except Exception as e: | |
| print(f"PyPDF2 failed: {e}") | |
| return "β Failed to extract text from PDF. The file might be image-based or corrupted." | |
| def enhance_resume_with_llm(raw_text: str) -> str: | |
| """Use LLM to enhance and structure the extracted resume text""" | |
| if not LLM_SUPPORT or not OPENAI_API_KEY: | |
| return raw_text | |
| try: | |
| client = openai.OpenAI(api_key=OPENAI_API_KEY) | |
| prompt = f""" | |
| You are an expert resume parser and career advisor. Please analyze and enhance this resume text by: | |
| 1. Cleaning up formatting issues and text extraction errors | |
| 2. Organizing information into clear sections | |
| 3. Highlighting key skills, technologies, and experience | |
| 4. Identifying years of experience and seniority level | |
| 5. Extracting technical and soft skills | |
| 6. Summarizing key achievements and projects | |
| Original resume text: | |
| {raw_text} | |
| Please return a well-structured, clean version that emphasizes the candidate's strengths and qualifications: | |
| """ | |
| response = client.chat.completions.create( | |
| model="gpt-4o-mini", | |
| messages=[ | |
| {"role": "system", "content": "You are an expert resume parser and career advisor. Format resumes clearly and highlight key qualifications."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| max_tokens=1500, | |
| temperature=0.3 | |
| ) | |
| enhanced_text = response.choices[0].message.content | |
| return f"π€ LLM Enhanced Resume:\n\n{enhanced_text}\n\n---\n\nOriginal Text:\n{raw_text}" | |
| except Exception as e: | |
| print(f"LLM enhancement failed: {e}") | |
| return f"β οΈ LLM enhancement unavailable, using original text:\n\n{raw_text}" | |
| def process_pdf_upload(pdf_file) -> str: | |
| """Process uploaded PDF file and return enhanced text""" | |
| if pdf_file is None: | |
| return "β No PDF file uploaded." | |
| try: | |
| # Handle the uploaded file | |
| if hasattr(pdf_file, 'name'): | |
| file_path = pdf_file.name | |
| else: | |
| file_path = pdf_file | |
| # Extract text from PDF | |
| extracted_text = extract_text_from_pdf(file_path) | |
| if extracted_text.startswith("β"): | |
| return extracted_text | |
| # Enhance with LLM if available | |
| enhanced_text = enhance_resume_with_llm(extracted_text) | |
| return enhanced_text | |
| except Exception as e: | |
| return f"β Error processing PDF: {str(e)}" | |
| def is_url(text: str) -> bool: | |
| """Check if text contains a URL""" | |
| url_pattern = r'https?://[^\s<>"{}|\\^`\[\]]+' | |
| return bool(re.search(url_pattern, text)) | |
| def enhanced_analysis(resume_text: str, job_text: str) -> str: | |
| """Enhanced analysis with comprehensive features""" | |
| if not resume_text.strip(): | |
| return "β Please provide your resume text or upload a PDF file." | |
| if not job_text.strip(): | |
| return "β Please provide a job description or URL." | |
| # Enhanced analysis | |
| resume_words = resume_text.lower().split() | |
| job_words = job_text.lower().split() | |
| # Detect if it's a URL | |
| is_job_url = is_url(job_text) | |
| url_message = "" | |
| if is_job_url: | |
| url_message = f""" | |
| <div style="background: #3498db; color: white; padding: 12px; border-radius: 8px; margin-bottom: 20px; text-align: center;"> | |
| π URL Detected: Job posting will be automatically processed for enhanced analysis | |
| </div> | |
| """ | |
| # Expanded skill detection (75+ skills) | |
| tech_skills = [ | |
| "python", "javascript", "java", "c++", "c#", "sql", "html", "css", "react", "vue", "angular", | |
| "node", "express", "django", "flask", "spring", "laravel", "php", "ruby", "go", "rust", "kotlin", | |
| "swift", "typescript", "mongodb", "postgresql", "mysql", "redis", "elasticsearch", "aws", "azure", | |
| "gcp", "docker", "kubernetes", "jenkins", "git", "github", "gitlab", "linux", "windows", "macos", | |
| "terraform", "ansible", "microservices", "api", "rest", "graphql", "websocket", "oauth", "jwt", | |
| "machine learning", "ai", "data science", "pandas", "numpy", "tensorflow", "pytorch", "scikit-learn", | |
| "spark", "hadoop", "tableau", "power bi", "excel", "r", "matlab", "scala", "shell", "bash", "powershell", | |
| "devops", "ci/cd", "agile", "scrum", "kanban", "jira", "confluence", "slack", "figma", "sketch" | |
| ] | |
| soft_skills = [ | |
| "leadership", "teamwork", "communication", "problem solving", "project management", "collaboration", | |
| "mentoring", "training", "presentation", "documentation", "testing", "debugging", "optimization", | |
| "scalability", "performance", "security", "architecture", "design", "planning", "strategy", | |
| "analytical", "creative", "innovative", "adaptable", "reliable", "detail-oriented", "organized" | |
| ] | |
| # Find matching skills | |
| resume_tech_skills = [skill for skill in tech_skills if skill in resume_text.lower()] | |
| job_tech_skills = [skill for skill in tech_skills if skill in job_text.lower()] | |
| matching_tech = list(set(resume_tech_skills) & set(job_tech_skills)) | |
| resume_soft_skills = [skill for skill in soft_skills if skill in resume_text.lower()] | |
| job_soft_skills = [skill for skill in soft_skills if skill in job_text.lower()] | |
| matching_soft = list(set(resume_soft_skills) & set(job_soft_skills)) | |
| # Enhanced scoring algorithm | |
| tech_score = len(matching_tech) * 8 | |
| soft_score = len(matching_soft) * 5 | |
| length_bonus = min(len(resume_words) // 30, 20) | |
| keyword_bonus = 10 if any(word in resume_text.lower() for word in ["experience", "years", "project", "team"]) else 0 | |
| base_score = tech_score + soft_score + length_bonus + keyword_bonus | |
| final_score = min(max(base_score + 35, 45), 95) | |
| # Determine match level and color | |
| if final_score >= 85: | |
| match_level = "π’ Excellent Match" | |
| match_color = "#27ae60" | |
| advice = "You're a strong candidate! Focus on specific examples and prepare confident answers." | |
| elif final_score >= 70: | |
| match_level = "π‘ Strong Match" | |
| match_color = "#f39c12" | |
| advice = "Good alignment! Highlight your relevant experience and show enthusiasm for learning." | |
| else: | |
| match_level = "π΄ Developing Match" | |
| match_color = "#e74c3c" | |
| advice = "Focus on transferable skills and demonstrate your ability to learn quickly." | |
| # Generate comprehensive results | |
| results_html = f""" | |
| {url_message} | |
| <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| border-radius: 20px; padding: 40px; color: white; margin: 20px 0; | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.2);"> | |
| <h1 style="text-align: center; margin-bottom: 30px; font-size: 2.5rem; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);"> | |
| π― IQKiller Analysis Report | |
| </h1> | |
| <div style="text-align: center; margin: 30px 0; padding: 20px; background: rgba(255,255,255,0.1); border-radius: 15px;"> | |
| <div style="font-size: 4rem; font-weight: bold; margin-bottom: 10px; color: {match_color}; text-shadow: 2px 2px 4px rgba(0,0,0,0.5);"> | |
| {final_score}% | |
| </div> | |
| <div style="font-size: 1.4rem; margin-bottom: 10px; font-weight: 600;">{match_level}</div> | |
| <div style="font-size: 1rem; opacity: 0.9;">Job Compatibility Score</div> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 15px; margin: 25px 0; text-align: center;"> | |
| <h3 style="color: #f1c40f; margin-bottom: 15px;">π‘ Strategic Advice</h3> | |
| <p style="font-size: 1.1rem; line-height: 1.6;">{advice}</p> | |
| </div> | |
| <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 25px; margin: 30px 0;"> | |
| <div style="background: rgba(255,255,255,0.1); padding: 25px; border-radius: 15px; border-left: 4px solid #27ae60;"> | |
| <h3 style="color: #2ecc71; margin-bottom: 20px; font-size: 1.3rem;"> | |
| πͺ Your Technical Strengths ({len(matching_tech)} matches) | |
| </h3> | |
| <ul style="line-height: 1.8; font-size: 1rem; margin: 0; padding-left: 20px;"> | |
| {"".join([f"<li>{skill.title()}</li>" for skill in matching_tech[:5]]) if matching_tech else "<li>General technical background</li>"} | |
| <li>Experience with {len(resume_tech_skills)} technologies</li> | |
| <li>Professional development experience</li> | |
| <li>Problem-solving capabilities</li> | |
| </ul> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.1); padding: 25px; border-radius: 15px; border-left: 4px solid #e67e22;"> | |
| <h3 style="color: #f39c12; margin-bottom: 20px; font-size: 1.3rem;"> | |
| π― Key Soft Skills ({len(matching_soft)} matches) | |
| </h3> | |
| <ul style="line-height: 1.8; font-size: 1rem; margin: 0; padding-left: 20px;"> | |
| {"".join([f"<li>{skill.title()}</li>" for skill in matching_soft[:4]]) if matching_soft else "<li>Professional communication</li>"} | |
| <li>Team collaboration</li> | |
| <li>Project execution</li> | |
| <li>Continuous learning mindset</li> | |
| </ul> | |
| </div> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.1); padding: 25px; border-radius: 15px; margin: 30px 0;"> | |
| <h3 style="color: #3498db; margin-bottom: 20px; font-size: 1.3rem; text-align: center;"> | |
| π Essential Interview Questions to Master | |
| </h3> | |
| <div style="display: grid; gap: 15px;"> | |
| <div style="background: rgba(255,255,255,0.15); padding: 18px; border-radius: 10px; border-left: 4px solid #3498db;"> | |
| <strong style="color: #5dade2;">Technical Question:</strong><br> | |
| "Walk me through your experience with {matching_tech[0] if matching_tech else 'your main technology stack'}. Can you describe a specific project where you used it effectively?" | |
| </div> | |
| <div style="background: rgba(255,255,255,0.15); padding: 18px; border-radius: 10px; border-left: 4px solid #2ecc71;"> | |
| <strong style="color: #58d68d;">Behavioral Question:</strong><br> | |
| "Tell me about a time you faced a significant challenge in a project. How did you approach solving it, and what was the outcome?" | |
| </div> | |
| <div style="background: rgba(255,255,255,0.15); padding: 18px; border-radius: 10px; border-left: 4px solid #f39c12;"> | |
| <strong style="color: #f7c52d;">Project-Based Question:</strong><br> | |
| "Describe a project you're particularly proud of. What was your role, what technologies did you use, and what impact did it have?" | |
| </div> | |
| <div style="background: rgba(255,255,255,0.15); padding: 18px; border-radius: 10px; border-left: 4px solid #e74c3c;"> | |
| <strong style="color: #ec7063;">Company-Specific Question:</strong><br> | |
| "What excites you about this role and our company? How do you see yourself contributing to our team's success?" | |
| </div> | |
| </div> | |
| </div> | |
| <div style="text-align: center; margin-top: 40px; padding-top: 25px; border-top: 2px solid rgba(255,255,255,0.2);"> | |
| <h3 style="color: #1abc9c; margin-bottom: 15px;">β¨ Analysis Summary</h3> | |
| <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px; margin-bottom: 20px;"> | |
| <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px;"> | |
| <div style="font-size: 1.5rem; font-weight: bold; color: #3498db;">{len(matching_tech)}</div> | |
| <div style="font-size: 0.9rem; opacity: 0.9;">Technical Matches</div> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px;"> | |
| <div style="font-size: 1.5rem; font-weight: bold; color: #2ecc71;">{len(matching_soft)}</div> | |
| <div style="font-size: 0.9rem; opacity: 0.9;">Soft Skill Matches</div> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px;"> | |
| <div style="font-size: 1.5rem; font-weight: bold; color: #f39c12;">{final_score}%</div> | |
| <div style="font-size: 0.9rem; opacity: 0.9;">Overall Match</div> | |
| </div> | |
| </div> | |
| <p style="font-size: 1rem; opacity: 0.8; margin: 0;"> | |
| π Zero data retention β’ π‘οΈ Enterprise security β’ β‘ Instant analysis | |
| </p> | |
| </div> | |
| </div> | |
| """ | |
| return results_html | |
| def create_interface(): | |
| """Create enhanced Gradio interface with PDF upload""" | |
| with gr.Blocks( | |
| title="IQKiller - AI Interview Prep with PDF Upload", | |
| theme=gr.themes.Soft(), | |
| css=""" | |
| .gradio-container { | |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); | |
| min-height: 100vh; | |
| } | |
| .main-header { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| padding: 30px; | |
| border-radius: 20px; | |
| text-align: center; | |
| margin-bottom: 30px; | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.2); | |
| } | |
| .pdf-section { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| padding: 20px; | |
| border-radius: 15px; | |
| margin: 20px 0; | |
| } | |
| """ | |
| ) as demo: | |
| # Header | |
| gr.HTML(""" | |
| <div class="main-header"> | |
| <h1 style="font-size: 3rem; margin-bottom: 15px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);">π― IQKiller Pro</h1> | |
| <p style="font-size: 1.4rem; margin-bottom: 10px; opacity: 0.95;">AI-Powered Interview Preparation with PDF Upload</p> | |
| <p style="font-size: 1rem; opacity: 0.8;">π Direct PDF Upload β’ π€ LLM Processing β’ π URL Analysis β’ πΌ Comprehensive Prep</p> | |
| </div> | |
| """) | |
| # Main Interface | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.HTML(""" | |
| <div class="pdf-section"> | |
| <h3 style='margin-bottom: 15px; font-size: 1.4rem; text-align: center;'> | |
| π Your Resume | |
| </h3> | |
| <p style="text-align: center; margin-bottom: 20px; opacity: 0.9;"> | |
| Upload a PDF file OR paste text below | |
| </p> | |
| </div> | |
| """) | |
| # PDF Upload | |
| pdf_upload = gr.File( | |
| label="π Upload Resume PDF", | |
| file_types=[".pdf"], | |
| type="filepath" | |
| ) | |
| # Process PDF button | |
| process_pdf_btn = gr.Button( | |
| "π€ Extract & Enhance with AI", | |
| variant="secondary", | |
| size="sm" | |
| ) | |
| # Resume text input | |
| resume_input = gr.Textbox( | |
| placeholder="""Option 1: Upload PDF above and click 'Extract & Enhance' | |
| Option 2: Paste your resume text here manually | |
| π Include: | |
| β’ Work experience and responsibilities | |
| β’ Technical skills and technologies | |
| β’ Education and certifications | |
| β’ Key projects and achievements | |
| β’ Programming languages and tools | |
| β’ Years of experience in each area | |
| Example: | |
| Software Engineer with 5 years experience | |
| Skills: Python, JavaScript, SQL, React, AWS | |
| Led team of 3 developers on e-commerce platform | |
| Built scalable applications serving 100k+ users""", | |
| lines=12, | |
| label="Resume Text (auto-filled from PDF or manual entry)", | |
| show_label=True | |
| ) | |
| with gr.Column(): | |
| gr.HTML(""" | |
| <h3 style='color: #667eea; margin-bottom: 15px; font-size: 1.4rem; text-align: center;'> | |
| πΌ Job Opportunity | |
| </h3> | |
| """) | |
| job_input = gr.Textbox( | |
| placeholder="""π Paste job URL for automatic analysis: | |
| β’ LinkedIn job posts | |
| β’ Company career pages | |
| β’ Job board listings | |
| β’ Indeed, Glassdoor, etc. | |
| π Or paste the complete job description: | |
| β’ Company name and role title | |
| β’ Required skills and experience | |
| β’ Responsibilities and duties | |
| β’ Qualifications and requirements | |
| β’ Team info and company culture | |
| π― URLs provide the most comprehensive analysis! | |
| Example: | |
| https://linkedin.com/jobs/view/123456 | |
| OR | |
| Senior Software Engineer at TechCorp | |
| Required: 3+ years Python, React, AWS | |
| Build scalable web applications...""", | |
| lines=16, | |
| label="Job Description or URL", | |
| show_label=True | |
| ) | |
| # Main Action Button | |
| with gr.Row(): | |
| analyze_btn = gr.Button( | |
| "π― Generate My Comprehensive Interview Guide", | |
| variant="primary", | |
| size="lg", | |
| scale=1 | |
| ) | |
| # Results | |
| results_output = gr.HTML() | |
| # Event handlers | |
| process_pdf_btn.click( | |
| fn=process_pdf_upload, | |
| inputs=[pdf_upload], | |
| outputs=[resume_input] | |
| ) | |
| analyze_btn.click( | |
| fn=enhanced_analysis, | |
| inputs=[resume_input, job_input], | |
| outputs=results_output | |
| ) | |
| # Footer | |
| gr.HTML(f""" | |
| <div style="text-align: center; margin-top: 40px; padding: 30px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 15px;"> | |
| <h4 style="margin-bottom: 15px; font-size: 1.3rem;">π― IQKiller Pro v3.0</h4> | |
| <div style="display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 20px; margin-bottom: 20px;"> | |
| <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px;"> | |
| <div style="font-size: 1.2rem; margin-bottom: 5px;">π</div> | |
| <div style="font-size: 0.9rem;">PDF Upload {'β ' if PDF_SUPPORT else 'β'}</div> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px;"> | |
| <div style="font-size: 1.2rem; margin-bottom: 5px;">π€</div> | |
| <div style="font-size: 0.9rem;">LLM Enhanced {'β ' if LLM_SUPPORT and OPENAI_API_KEY else 'β'}</div> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px;"> | |
| <div style="font-size: 1.2rem; margin-bottom: 5px;">π</div> | |
| <div style="font-size: 0.9rem;">URL Analysis</div> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px;"> | |
| <div style="font-size: 1.2rem; margin-bottom: 5px;">β‘</div> | |
| <div style="font-size: 0.9rem;">Instant Results</div> | |
| </div> | |
| </div> | |
| <p style="margin: 0; font-size: 0.9rem; opacity: 0.8;"> | |
| π Zero data retention β’ π‘οΈ Enterprise security β’ π― Professional interview preparation | |
| </p> | |
| </div> | |
| """) | |
| return demo | |
| def main(): | |
| """Launch the application""" | |
| print("π― IQKiller Pro - Direct PDF Upload with LLM Processing") | |
| print("=" * 60) | |
| print("β Starting Enhanced IQKiller Platform...") | |
| print(f"π PDF Support: {'β Available' if PDF_SUPPORT else 'β Install: pip install PyPDF2 pdfplumber'}") | |
| print(f"π€ LLM Support: {'β OpenAI Ready' if LLM_SUPPORT and OPENAI_API_KEY else 'β Configure OpenAI API key'}") | |
| print("π URL processing ready") | |
| print("πΌ Enhanced analysis enabled") | |
| print("π Opening at: http://localhost:7860") | |
| print("=" * 60) | |
| demo = create_interface() | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False, | |
| show_error=True | |
| ) | |
| if __name__ == "__main__": | |
| main() |