IQKillerv2 / simple_iqkiller.py
AvikalpK's picture
πŸš€ Enhanced IQKiller with Next.js Vercel version
0939a57
Raw
History Blame Contribute Delete
44.3 kB
#!/usr/bin/env python3
"""
IQKiller - Simplified Complete Platform
All core functionality with Apple-inspired UI, avoiding Gradio compatibility issues
"""
import gradio as gr
import asyncio
import time
import json
import re
from typing import Dict, Any, Optional, Tuple
# Configuration and API setup
import os
# Set up API keys from environment
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY")
SERPAPI_KEY = os.getenv("SERPAPI_KEY")
# PDF processing imports
try:
import PyPDF2
import pdfplumber
PDF_AVAILABLE = True
except ImportError:
PDF_AVAILABLE = False
# Import our modules with error handling
try:
from salary_negotiation_simulator import get_simulator, get_random_scenario, evaluate_scenario_answer
negotiation_available = True
except ImportError:
negotiation_available = False
try:
from llm_client import get_llm_client
llm_available = True
except ImportError:
llm_available = False
# Import comprehensive interview guide generator
try:
from interview_guide_generator import ComprehensiveAnalyzer, format_interview_guide_html
comprehensive_analyzer = ComprehensiveAnalyzer()
comprehensive_available = True
except ImportError:
comprehensive_available = False
comprehensive_analyzer = None
# Import URL scraping functionality
try:
from micro.scrape import scrape_job_url, get_optimal_scraping_method
scraping_available = True
except ImportError:
scraping_available = False
# URL detection
def is_url(text: str) -> bool:
"""Check if text is a URL"""
import re
url_pattern = re.compile(
r'^https?://' # http:// or https://
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|' # domain...
r'localhost|' # localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
r'(?::\d+)?' # optional port
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
return bool(url_pattern.match(text.strip()))
# PDF text extraction
def extract_text_from_pdf(pdf_file) -> str:
"""Extract text from uploaded PDF file"""
if not PDF_AVAILABLE:
return "❌ PDF processing not available. Please install PyPDF2 and pdfplumber."
if pdf_file is None:
return ""
try:
# Try with pdfplumber first (better text extraction)
with pdfplumber.open(pdf_file.name) as pdf:
text = ""
for page in pdf.pages:
page_text = page.extract_text()
if page_text:
text += page_text + "\n"
if text.strip():
return text.strip()
except Exception as e:
print(f"Pdfplumber failed: {e}, trying PyPDF2...")
try:
# Fallback to PyPDF2
with open(pdf_file.name, 'rb') as file:
pdf_reader = PyPDF2.PdfReader(file)
text = ""
for page_num in range(len(pdf_reader.pages)):
page = pdf_reader.pages[page_num]
page_text = page.extract_text()
if page_text:
text += page_text + "\n"
return text.strip()
except Exception as e:
return f"❌ Failed to extract text from PDF: {str(e)}"
def combine_resume_sources(resume_text: str, pdf_file) -> str:
"""Combine text from manual input and PDF upload"""
combined_text = ""
# Add manual text input
if resume_text and resume_text.strip():
combined_text += resume_text.strip() + "\n\n"
# Add PDF text if uploaded
if pdf_file is not None:
pdf_text = extract_text_from_pdf(pdf_file)
if pdf_text and not pdf_text.startswith("❌"):
combined_text += "=== EXTRACTED FROM PDF ===\n"
combined_text += pdf_text
elif pdf_text.startswith("❌"):
return pdf_text # Return error message
if not combined_text.strip():
return ""
return combined_text.strip()
# Apple-inspired CSS
APPLE_CSS = """
/* === APPLE-INSPIRED DESIGN === */
:root {
--apple-blue: #007AFF;
--apple-blue-dark: #0051D5;
--apple-gray: #8E8E93;
--apple-light-gray: #F2F2F7;
--apple-green: #34C759;
--apple-orange: #FF9500;
--apple-red: #FF3B30;
--glass-bg: rgba(255, 255, 255, 0.1);
--glass-border: rgba(255, 255, 255, 0.2);
--shadow-soft: 0 8px 32px rgba(0, 0, 0, 0.1);
--shadow-medium: 0 16px 64px rgba(0, 0, 0, 0.15);
}
.gradio-container {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
min-height: 100vh;
}
.container {
background: var(--glass-bg) !important;
backdrop-filter: blur(20px) !important;
-webkit-backdrop-filter: blur(20px) !important;
border: 1px solid var(--glass-border) !important;
border-radius: 20px !important;
box-shadow: var(--shadow-medium) !important;
margin: 20px !important;
padding: 30px !important;
}
.main-header {
text-align: center;
margin-bottom: 40px;
color: white;
}
.main-title {
font-size: 3rem !important;
font-weight: 700 !important;
background: linear-gradient(45deg, #fff, #e0e0e0) !important;
-webkit-background-clip: text !important;
-webkit-text-fill-color: transparent !important;
margin-bottom: 10px !important;
}
.glass-panel {
background: var(--glass-bg) !important;
backdrop-filter: blur(15px) !important;
-webkit-backdrop-filter: blur(15px) !important;
border: 1px solid var(--glass-border) !important;
border-radius: 16px !important;
box-shadow: var(--shadow-soft) !important;
padding: 24px !important;
margin: 16px 0 !important;
}
.gr-textbox, .gr-textarea {
background: var(--glass-bg) !important;
border: 1px solid var(--glass-border) !important;
border-radius: 12px !important;
color: white !important;
backdrop-filter: blur(10px) !important;
}
.gr-button {
background: var(--apple-blue) !important;
border: none !important;
border-radius: 12px !important;
color: white !important;
font-weight: 600 !important;
padding: 12px 24px !important;
transition: all 0.3s ease !important;
}
.gr-button:hover {
background: var(--apple-blue-dark) !important;
transform: translateY(-2px) !important;
}
.result-card {
background: var(--glass-bg) !important;
border: 1px solid var(--glass-border) !important;
border-radius: 16px !important;
padding: 24px !important;
margin: 16px 0 !important;
backdrop-filter: blur(15px) !important;
box-shadow: var(--shadow-soft) !important;
}
.match-score {
font-size: 3rem !important;
font-weight: 700 !important;
text-align: center !important;
background: linear-gradient(45deg, var(--apple-green), var(--apple-blue)) !important;
-webkit-background-clip: text !important;
-webkit-text-fill-color: transparent !important;
}
@keyframes slideInUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
.slide-in { animation: slideInUp 0.6s ease-out; }
html { scroll-behavior: smooth; }
"""
# Auto-scroll JavaScript
AUTO_SCROLL_JS = """
function autoScrollToResults() {
setTimeout(() => {
const targets = [
document.querySelector('.result-card'),
document.querySelector('.match-score'),
document.querySelector('.glass-panel')
];
for (let target of targets) {
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
break;
}
}
// Fallback: scroll to top
setTimeout(() => window.scrollTo({ top: 0, behavior: 'smooth' }), 200);
}, 500);
return "Scrolling to results...";
}
"""
def create_status_display() -> str:
"""Create system status display"""
openai_status = "🟒" if OPENAI_API_KEY else "πŸ”΄"
anthropic_status = "🟒" if ANTHROPIC_API_KEY else "🟑"
serp_status = "🟒" if SERPAPI_KEY else "🟑"
return f"""
<div class="glass-panel" style="text-align: center; margin-bottom: 20px;">
<h3 style="color: white; margin-bottom: 15px;">πŸ”§ System Status</h3>
<div style="display: flex; justify-content: space-around; flex-wrap: wrap;">
<div style="color: rgba(255,255,255,0.9); margin: 5px;">
{openai_status} OpenAI: {"Ready" if OPENAI_API_KEY else "Missing"}
</div>
<div style="color: rgba(255,255,255,0.9); margin: 5px;">
{anthropic_status} Anthropic: {"Ready" if ANTHROPIC_API_KEY else "Optional"}
</div>
<div style="color: rgba(255,255,255,0.9); margin: 5px;">
{serp_status} SerpAPI: {"Ready" if SERPAPI_KEY else "Optional"}
</div>
<div style="color: rgba(255,255,255,0.9); margin: 5px;">
πŸ”— URL Scraping: {"Ready" if scraping_available else "Limited"}
</div>
<div style="color: rgba(255,255,255,0.9); margin: 5px;">
🎯 Negotiation: {"Ready" if negotiation_available else "Limited"}
</div>
</div>
</div>
"""
def simple_resume_analysis(resume_text: str) -> dict:
"""Simple resume analysis with keyword extraction"""
if not resume_text.strip():
return {"skills": [], "experience": 0, "roles": []}
# Extract skills
tech_skills = ["Python", "JavaScript", "Java", "SQL", "React", "Node.js", "AWS", "Docker", "Git"]
soft_skills = ["Leadership", "Communication", "Project Management", "Team Work", "Problem Solving"]
found_skills = []
for skill in tech_skills + soft_skills:
if skill.lower() in resume_text.lower():
found_skills.append(skill)
# Extract experience years
experience_match = re.search(r'(\d+)[\s\+]*years?\s+(?:of\s+)?experience', resume_text, re.IGNORECASE)
experience_years = int(experience_match.group(1)) if experience_match else 2
# Extract roles (simplified)
role_keywords = ["engineer", "developer", "manager", "analyst", "scientist", "designer"]
found_roles = []
for keyword in role_keywords:
if keyword in resume_text.lower():
found_roles.append(keyword.title())
return {
"skills": found_skills,
"experience": experience_years,
"roles": found_roles or ["Professional"]
}
async def smart_job_analysis(job_input: str) -> dict:
"""Smart job analysis with URL scraping support"""
if not job_input.strip():
return {"company": "Unknown", "role": "Unknown", "required_skills": [], "location": "Remote", "source": "empty"}
job_text = job_input.strip()
source_info = {"source": "text"}
# Check if input is a URL and scrape if available
if is_url(job_text) and scraping_available:
try:
print(f"πŸ”„ Detected URL: {job_text}")
print(f"πŸ” Scraping with optimal method...")
# Get optimal scraping method for this URL
method = get_optimal_scraping_method(job_text)
print(f"πŸ“‘ Using {method} method for scraping")
# Scrape the URL
scrape_result = await scrape_job_url(job_text, prefer_method=method)
if scrape_result.success and scrape_result.content:
job_text = scrape_result.content
source_info = {
"source": "scraped",
"url": job_input, # Store original URL
"method": scrape_result.method,
"processing_time": scrape_result.processing_time,
"content_length": len(scrape_result.content),
"scraped_text": scrape_result.content # Include scraped content
}
print(f"βœ… Successfully scraped {len(job_text)} characters using {scrape_result.method}")
else:
print(f"⚠️ Scraping failed: {scrape_result.error}")
print("πŸ“ Falling back to treating input as job description text")
job_text = job_input # Fallback to original input
source_info["source"] = "text_fallback"
except Exception as e:
print(f"❌ Scraping error: {e}")
job_text = job_input # Fallback to original input
source_info["source"] = "text_fallback"
# Extract company (enhanced patterns)
company_patterns = [
r'at\s+([A-Z][a-zA-Z\s&\.]+?)(?:\s|$|,|\n)',
r'([A-Z][a-zA-Z\s&\.]+?)\s+is\s+(?:hiring|looking)',
r'join\s+([A-Z][a-zA-Z\s&\.]+?)(?:\s|$|,|\n)',
r'company:\s*([A-Z][a-zA-Z\s&\.]+?)(?:\s|$|,|\n)',
r'([A-Z][a-zA-Z\s&\.]+?)\s+(?:job|position|role)',
# Common company patterns
r'(spotify|google|amazon|microsoft|meta|apple|netflix|uber|airbnb)',
]
company = "Unknown Company"
for pattern in company_patterns:
match = re.search(pattern, job_text, re.IGNORECASE)
if match:
company = match.group(1).strip()
# Clean up common suffixes
company = re.sub(r'\s+(is|has|we|the|a|an).*$', '', company, flags=re.IGNORECASE)
break
# Extract role (enhanced patterns)
role_patterns = [
r'(senior\s+)?(data\s+scientist|software\s+engineer|product\s+manager|frontend\s+developer|backend\s+developer|full\s+stack|machine\s+learning\s+engineer|devops\s+engineer|site\s+reliability\s+engineer)',
r'position[:\s]+(senior\s+)?([a-zA-Z\s]+)',
r'role[:\s]+(senior\s+)?([a-zA-Z\s]+)',
r'job\s+title[:\s]+(senior\s+)?([a-zA-Z\s]+)',
r'we\'re\s+looking\s+for\s+(?:a\s+)?(senior\s+)?([a-zA-Z\s]+)',
r'hiring\s+(?:a\s+)?(senior\s+)?([a-zA-Z\s]+)',
]
role = "Unknown Role"
seniority = "Mid-level"
for pattern in role_patterns:
match = re.search(pattern, job_text, re.IGNORECASE)
if match:
groups = match.groups()
if len(groups) >= 2:
senior_part = groups[0] or ""
role_part = groups[1] or groups[-1]
if "senior" in senior_part.lower():
seniority = "Senior"
role = (senior_part + role_part).strip().title()
break
# Extract required skills (expanded)
tech_skills = [
"Python", "JavaScript", "Java", "SQL", "React", "Node.js", "AWS", "Docker", "Git",
"Machine Learning", "Data Science", "Analytics", "R", "Tableau", "Pandas", "NumPy",
"TensorFlow", "PyTorch", "Kubernetes", "MongoDB", "PostgreSQL", "Redis", "Apache Spark",
"Scala", "Go", "Rust", "TypeScript", "Vue.js", "Angular", "Django", "Flask", "Express",
"GraphQL", "REST API", "Microservices", "CI/CD", "Jenkins", "Terraform", "Ansible"
]
required_skills = []
for skill in tech_skills:
if skill.lower() in job_text.lower():
required_skills.append(skill)
# Extract location (enhanced)
location = "Remote"
location_patterns = [
r'location[:\s]+([a-zA-Z\s,]+)',
r'based\s+in\s+([a-zA-Z\s,]+)',
r'([a-zA-Z\s]+),\s*([A-Z]{2})',
r'(remote|hybrid|on-site)',
r'(san francisco|new york|seattle|austin|boston|chicago|los angeles|denver|atlanta|miami)',
]
for pattern in location_patterns:
match = re.search(pattern, job_text, re.IGNORECASE)
if match:
location = match.group(1).strip().title()
break
# Determine industry
industry = "Technology"
if any(keyword in job_text.lower() for keyword in ["spotify", "music", "streaming", "audio"]):
industry = "Music & Entertainment"
elif any(keyword in job_text.lower() for keyword in ["finance", "bank", "trading", "fintech"]):
industry = "Finance"
elif any(keyword in job_text.lower() for keyword in ["healthcare", "medical", "biotech", "pharma"]):
industry = "Healthcare"
elif any(keyword in job_text.lower() for keyword in ["retail", "e-commerce", "shopping"]):
industry = "Retail & E-commerce"
result = {
"company": company,
"role": role,
"required_skills": required_skills,
"location": location,
"industry": industry,
"seniority": seniority,
**source_info
}
return result
def simple_job_analysis(job_text: str) -> dict:
"""Legacy function - synchronous job analysis"""
if not job_text.strip():
return {"company": "Unknown", "role": "Unknown", "required_skills": [], "location": "Remote", "source": "empty"}
# Basic synchronous analysis (fallback)
import re
# Extract company (simple patterns)
company_patterns = [
r'at\s+([A-Z][a-zA-Z\s&\.]+?)(?:\s|$|,|\n)',
r'([A-Z][a-zA-Z\s&\.]+?)\s+is\s+(?:hiring|looking)',
r'join\s+([A-Z][a-zA-Z\s&\.]+?)(?:\s|$|,|\n)',
]
company = "Unknown Company"
for pattern in company_patterns:
match = re.search(pattern, job_text, re.IGNORECASE)
if match:
company = match.group(1).strip()
break
# Extract role
role_patterns = [
r'(senior\s+)?(data\s+scientist|software\s+engineer|product\s+manager)',
r'position[:\s]+(senior\s+)?([a-zA-Z\s]+)',
r'role[:\s]+(senior\s+)?([a-zA-Z\s]+)',
]
role = "Unknown Role"
for pattern in role_patterns:
match = re.search(pattern, job_text, re.IGNORECASE)
if match:
groups = match.groups()
if len(groups) >= 2:
senior_part = groups[0] or ""
role_part = groups[1] or groups[-1]
role = (senior_part + role_part).strip().title()
break
# Extract required skills
tech_skills = ["Python", "JavaScript", "Java", "SQL", "React", "Node.js", "AWS", "Docker", "Git", "Machine Learning"]
required_skills = []
for skill in tech_skills:
if skill.lower() in job_text.lower():
required_skills.append(skill)
return {
"company": company,
"role": role,
"required_skills": required_skills,
"location": "Remote",
"industry": "Technology",
"seniority": "Mid-level",
"source": "text"
}
def calculate_match_score(resume_data: dict, job_data: dict) -> float:
"""Calculate compatibility match score"""
resume_skills = set(skill.lower() for skill in resume_data["skills"])
job_skills = set(skill.lower() for skill in job_data["required_skills"])
if not job_skills:
return 75.0 # Default score if no skills detected
# Calculate skill overlap
skill_overlap = len(resume_skills & job_skills)
skill_score = (skill_overlap / len(job_skills)) * 100 if job_skills else 50
# Experience factor
experience_score = min(resume_data["experience"] * 10, 100)
# Combine scores
final_score = (skill_score * 0.7) + (experience_score * 0.3)
return min(max(final_score, 30), 95) # Ensure reasonable bounds
async def analyze_job_compatibility(resume_text: str, job_input: str) -> Tuple[str, str, str]:
"""Quick analysis function (30 seconds)"""
if not resume_text.strip():
return "❌ Please provide your resume text.", "", ""
if not job_input.strip():
return "❌ Please provide a job URL or job description.", "", ""
# Show processing indicator
processing_html = """
<div class="glass-panel" style="text-align: center;">
<h3 style="color: white;">⚑ Quick Analysis...</h3>
<div style="margin: 20px 0;">
<div style="display: inline-block; width: 60px; height: 60px; border: 4px solid rgba(255,255,255,0.3); border-radius: 50%; border-top-color: #007AFF; animation: spin 1s linear infinite;"></div>
</div>
<p style="color: rgba(255,255,255,0.8);">Parsing resume β€’ Analyzing job β€’ Generating insights</p>
</div>
<style>
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
</style>
"""
try:
# Simulate processing time
await asyncio.sleep(2)
# Analyze resume and job (legacy - simple analysis only)
resume_data = simple_resume_analysis(resume_text)
job_data = simple_job_analysis(job_input)
# Calculate match score
match_score = calculate_match_score(resume_data, job_data)
# Generate insights
skill_matches = list(set(resume_data["skills"]) & set(job_data["required_skills"]))
skill_gaps = list(set(job_data["required_skills"]) - set(resume_data["skills"]))
# Create results HTML
results_html = f"""
<div class="result-card slide-in">
<div class="match-score">{match_score:.0f}%</div>
<div style="text-align: center; color: rgba(255,255,255,0.8); font-size: 1.1rem; margin-bottom: 30px;">
Job Match Score
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 30px;">
<div>
<h4 style="color: var(--apple-green); margin-bottom: 15px;">πŸ’ͺ Your Strengths</h4>
<ul style="color: rgba(255,255,255,0.9); line-height: 1.6;">
<li>{resume_data["experience"]} years of professional experience</li>
<li>Skills in {', '.join(skill_matches[:3]) if skill_matches else 'various technologies'}</li>
<li>Background in {', '.join(resume_data["roles"][:2])}</li>
<li>Strong technical foundation</li>
</ul>
</div>
<div>
<h4 style="color: var(--apple-orange); margin-bottom: 15px;">🎯 Areas to Address</h4>
<ul style="color: rgba(255,255,255,0.9); line-height: 1.6;">
{"".join([f"<li>Consider learning {skill}</li>" for skill in skill_gaps[:3]]) if skill_gaps else "<li>Continue strengthening current skills</li>"}
<li>Practice interview storytelling</li>
<li>Research the company culture</li>
</ul>
</div>
</div>
<div style="margin-top: 30px;">
<h4 style="color: var(--apple-blue); margin-bottom: 15px;">πŸ“‹ Interview Questions to Prepare</h4>
<div style="color: rgba(255,255,255,0.9);">
<div style="margin-bottom: 10px; padding: 12px; background: var(--glass-bg); border-radius: 8px;">
<strong>Technical:</strong> Tell me about your experience with {skill_matches[0] if skill_matches else 'your main technology stack'}
</div>
<div style="margin-bottom: 10px; padding: 12px; background: var(--glass-bg); border-radius: 8px;">
<strong>Behavioral:</strong> Describe a challenging project you worked on and how you overcame obstacles
</div>
<div style="margin-bottom: 10px; padding: 12px; background: var(--glass-bg); border-radius: 8px;">
<strong>Experience:</strong> How do you handle working in a team environment?
</div>
<div style="margin-bottom: 10px; padding: 12px; background: var(--glass-bg); border-radius: 8px;">
<strong>Role-specific:</strong> What interests you about working at {job_data["company"]}?
</div>
</div>
</div>
<div style="margin-top: 30px;">
<h4 style="color: var(--apple-green); margin-bottom: 15px;">πŸ’° Salary Insights</h4>
<div style="background: var(--glass-bg); padding: 16px; border-radius: 12px; color: rgba(255,255,255,0.9);">
<p><strong>Experience Level:</strong> {resume_data["experience"]} years qualifies for mid-level positions</p>
<p><strong>Negotiation Tip:</strong> Highlight your {skill_matches[0] if skill_matches else 'technical'} skills and experience</p>
<p><strong>Market Position:</strong> {"Strong" if match_score > 80 else "Good" if match_score > 60 else "Developing"} candidate profile</p>
</div>
</div>
<div style="margin-top: 30px;">
<h4 style="color: white; margin-bottom: 15px;">πŸš€ Next Steps</h4>
<ul style="color: rgba(255,255,255,0.9); line-height: 1.6;">
<li>Practice answers to the suggested interview questions</li>
<li>Research {job_data["company"]} company background and values</li>
<li>Prepare specific examples using the STAR method</li>
<li>{"Consider learning " + skill_gaps[0] if skill_gaps else "Continue strengthening your skill set"}</li>
</ul>
</div>
<div style="margin-top: 20px; text-align: center; color: rgba(255,255,255,0.6); font-size: 0.9rem;">
Analysis completed β€’ Confidence: {"High" if match_score > 80 else "Medium" if match_score > 60 else "Good"}
</div>
</div>
"""
# Create negotiation scenario if available
negotiation_html = ""
if negotiation_available:
try:
scenario = get_random_scenario()
negotiation_html = f"""
<div style="background: linear-gradient(135deg, var(--apple-orange), var(--apple-red)); color: white; border-radius: 16px; padding: 24px; margin: 16px 0; box-shadow: var(--shadow-medium);" class="slide-in">
<h3 style="margin-bottom: 20px;">πŸ’Ό Salary Negotiation Practice</h3>
<h4 style="margin-bottom: 15px;">{scenario.title}</h4>
<p style="margin-bottom: 20px; line-height: 1.6;">{scenario.situation}</p>
<p style="font-weight: 600; margin-bottom: 20px;">{scenario.question}</p>
<div style="margin-top: 15px; font-size: 0.9rem; opacity: 0.8;">
πŸ’‘ Practice different negotiation scenarios to improve your skills!
<br>Difficulty: {scenario.difficulty} β€’ Type: {scenario.type.value.replace('_', ' ').title()}
</div>
</div>
"""
except Exception:
negotiation_html = """
<div style="background: var(--glass-bg); border-radius: 16px; padding: 24px; margin: 16px 0;" class="slide-in">
<h3 style="color: white; margin-bottom: 15px;">πŸ’Ό Salary Negotiation Tips</h3>
<ul style="color: rgba(255,255,255,0.9); line-height: 1.6;">
<li>Research market rates for your role and experience level</li>
<li>Prepare to articulate your value proposition</li>
<li>Consider the full compensation package, not just base salary</li>
<li>Practice negotiation scenarios with friends or mentors</li>
</ul>
</div>
"""
return results_html, negotiation_html, AUTO_SCROLL_JS
except Exception as e:
error_html = f"""
<div class="result-card">
<h3 style="color: var(--apple-red);">❌ Analysis Error</h3>
<p style="color: rgba(255,255,255,0.8);">
We encountered an issue: {str(e)}
</p>
<p style="color: rgba(255,255,255,0.6); font-size: 0.9rem;">
Please check your inputs and try again.
</p>
</div>
"""
return error_html, "", ""
async def generate_comprehensive_guide(resume_text: str, job_input: str) -> Tuple[str, str, str]:
"""Generate comprehensive interview guide with URL scraping support"""
if not resume_text.strip():
return "❌ Please provide your resume text.", "", ""
if not job_input.strip():
return "❌ Please provide a job URL or job description.", "", ""
# Show enhanced processing indicator
is_url_input = is_url(job_input.strip())
processing_message = "πŸ”— Scraping job posting β€’ Analyzing resume β€’ Generating comprehensive guide..." if is_url_input else "πŸ“ Analyzing resume & job β€’ Generating comprehensive guide..."
processing_html = f"""
<div class="glass-panel" style="text-align: center;">
<h3 style="color: white;">🎯 Creating Your Personalized Interview Guide...</h3>
<div style="margin: 20px 0;">
<div style="display: inline-block; width: 60px; height: 60px; border: 4px solid rgba(255,255,255,0.3); border-radius: 50%; border-top-color: #007AFF; animation: spin 1s linear infinite;"></div>
</div>
<p style="color: rgba(255,255,255,0.8);">{processing_message}</p>
</div>
<style>
@keyframes spin {{ 0% {{ transform: rotate(0deg); }} 100% {{ transform: rotate(360deg); }} }}
</style>
"""
try:
# Simulate processing time (longer for URL scraping)
await asyncio.sleep(4 if is_url_input else 3)
# Smart job analysis with URL scraping
resume_data = simple_resume_analysis(resume_text)
job_data = await smart_job_analysis(job_input)
# Extract scraped content for comprehensive analysis
scraped_content = job_input # default to original input
if job_data.get("source") == "scraped" and "scraped_text" in job_data:
scraped_content = job_data["scraped_text"]
# Add scraping status to display
scraping_status = ""
if job_data.get("source") == "scraped":
scraping_status = f"""
<div style="background: var(--apple-green); color: white; padding: 10px; border-radius: 8px; margin: 10px 0; text-align: center;">
βœ… Successfully scraped job posting using {job_data.get('method', 'unknown')} method
({job_data.get('content_length', 0)} characters in {job_data.get('processing_time', 0):.1f}s)
</div>
"""
elif job_data.get("source") == "text_fallback":
scraping_status = f"""
<div style="background: var(--apple-orange); color: white; padding: 10px; border-radius: 8px; margin: 10px 0; text-align: center;">
⚠️ URL scraping failed, analyzing as text description
</div>
"""
# Use comprehensive analyzer if available
if comprehensive_available and comprehensive_analyzer:
# Use scraped content if available, otherwise use original input
guide = comprehensive_analyzer.generate_comprehensive_guide(resume_text, scraped_content)
results_html = scraping_status + format_interview_guide_html(guide)
else:
# Fallback to enhanced simple analysis
results_html = scraping_status + await generate_enhanced_simple_analysis(resume_text, job_input)
# Create negotiation scenario if available
negotiation_html = ""
if negotiation_available:
try:
scenario = get_random_scenario()
negotiation_html = f"""
<div style="background: linear-gradient(135deg, var(--apple-orange), var(--apple-red)); color: white; border-radius: 16px; padding: 24px; margin: 16px 0; box-shadow: var(--shadow-medium);" class="slide-in">
<h3 style="margin-bottom: 20px;">πŸ’Ό Salary Negotiation Practice</h3>
<h4 style="margin-bottom: 15px;">{scenario.title}</h4>
<p style="margin-bottom: 20px; line-height: 1.6;">{scenario.situation}</p>
<p style="font-weight: 600; margin-bottom: 20px;">{scenario.question}</p>
<div style="margin-top: 15px; font-size: 0.9rem; opacity: 0.8;">
πŸ’‘ Practice different negotiation scenarios to improve your skills!
<br>Difficulty: {scenario.difficulty} β€’ Type: {scenario.type.value.replace('_', ' ').title()}
</div>
</div>
"""
except Exception:
negotiation_html = """
<div style="background: var(--glass-bg); border-radius: 16px; padding: 24px; margin: 16px 0;" class="slide-in">
<h3 style="color: white; margin-bottom: 15px;">πŸ’Ό Salary Negotiation Tips</h3>
<ul style="color: rgba(255,255,255,0.9); line-height: 1.6;">
<li>Research market rates for your role and experience level</li>
<li>Prepare to articulate your value proposition</li>
<li>Consider the full compensation package, not just base salary</li>
<li>Practice negotiation scenarios with friends or mentors</li>
</ul>
</div>
"""
return results_html, negotiation_html, AUTO_SCROLL_JS
except Exception as e:
error_html = f"""
<div class="result-card">
<h3 style="color: var(--apple-red);">❌ Analysis Error</h3>
<p style="color: rgba(255,255,255,0.8);">
We encountered an issue: {str(e)}
</p>
<p style="color: rgba(255,255,255,0.6); font-size: 0.9rem;">
Please check your inputs and try again.
</p>
</div>
"""
return error_html, "", ""
async def generate_enhanced_simple_analysis(resume_text: str, job_input: str) -> str:
"""Enhanced simple analysis as fallback"""
resume_data = simple_resume_analysis(resume_text)
job_data = simple_job_analysis(job_input)
match_score = calculate_match_score(resume_data, job_data)
# Generate comprehensive-style output with simple analysis
return f"""
<div class="result-card slide-in" style="max-width: 1200px; margin: 0 auto;">
<h1 style="color: white; text-align: center; margin-bottom: 20px;">Enhanced Interview Guide: {job_data['role']} at {job_data['company']}</h1>
<div style="text-align: center; margin-bottom: 30px;">
<div style="font-size: 1.2rem; color: var(--apple-green); font-weight: 600; margin-bottom: 10px;">
Match Score: {"🟒 Excellent Match" if match_score >= 85 else "🟑 Good Match" if match_score >= 70 else "πŸ”΄ Developing Match"} ({match_score:.1f}%)
</div>
</div>
<h2 style="color: white; margin-bottom: 20px;">πŸ“– Introduction</h2>
<p style="color: rgba(255,255,255,0.9); line-height: 1.6; margin-bottom: 30px;">
This {job_data['role']} position at {job_data['company']} represents an excellent opportunity for someone with your background.
With {resume_data['experience']} years of experience and skills in {', '.join(resume_data['skills'][:3]) if resume_data['skills'] else 'various technologies'},
you're well-positioned to contribute meaningfully to their team. Your technical foundation and experience make you a strong candidate for this role.
</p>
<h2 style="color: white; margin-bottom: 20px;">🎯 Skills Assessment</h2>
<div style="background: var(--glass-bg); padding: 20px; border-radius: 12px; margin-bottom: 30px;">
<p style="color: rgba(255,255,255,0.9); margin-bottom: 15px;">
<strong>Your Strengths:</strong> {', '.join(list(set(resume_data['skills']) & set(job_data['required_skills']))[:5]) if set(resume_data['skills']) & set(job_data['required_skills']) else 'Technical foundation, problem-solving skills'}
</p>
<p style="color: rgba(255,255,255,0.9);">
<strong>Areas to Develop:</strong> {', '.join(list(set(job_data['required_skills']) - set(resume_data['skills']))[:3]) if set(job_data['required_skills']) - set(resume_data['skills']) else 'Continue strengthening existing skills'}
</p>
</div>
<h2 style="color: white; margin-bottom: 20px;">πŸ“‹ Interview Questions to Prepare</h2>
<div style="margin-bottom: 30px;">
<div style="margin-bottom: 20px; padding: 16px; background: var(--glass-bg); border-radius: 12px; border-left: 4px solid var(--apple-blue);">
<h4 style="color: var(--apple-orange); margin-bottom: 10px;">Technical Question</h4>
<p style="color: rgba(255,255,255,0.9);">Tell me about your experience with {list(set(resume_data['skills']) & set(job_data['required_skills']))[0] if set(resume_data['skills']) & set(job_data['required_skills']) else 'your main technology stack'}.</p>
</div>
<div style="margin-bottom: 20px; padding: 16px; background: var(--glass-bg); border-radius: 12px; border-left: 4px solid var(--apple-green);">
<h4 style="color: var(--apple-orange); margin-bottom: 10px;">Behavioral Question</h4>
<p style="color: rgba(255,255,255,0.9);">Describe a challenging project you worked on and how you overcame obstacles.</p>
</div>
<div style="margin-bottom: 20px; padding: 16px; background: var(--glass-bg); border-radius: 12px; border-left: 4px solid var(--apple-orange);">
<h4 style="color: var(--apple-orange); margin-bottom: 10px;">Company Question</h4>
<p style="color: rgba(255,255,255,0.9);">What interests you about working at {job_data['company']}?</p>
</div>
</div>
<h2 style="color: white; margin-bottom: 20px;">πŸš€ Preparation Strategy</h2>
<div style="background: var(--glass-bg); padding: 20px; border-radius: 12px; margin-bottom: 30px;">
<ul style="color: rgba(255,255,255,0.9); line-height: 1.6;">
<li>Research {job_data['company']} company background and recent developments</li>
<li>Prepare specific examples using the STAR method (Situation, Task, Action, Result)</li>
<li>Practice explaining your technical experience clearly</li>
<li>Prepare thoughtful questions about the role and team</li>
</ul>
</div>
<div style="text-align: center; margin-top: 30px; color: rgba(255,255,255,0.6); font-size: 0.9rem;">
<p><em>Enhanced analysis completed β€’ Your match score of {match_score:.1f}% indicates {"strong" if match_score >= 80 else "good" if match_score >= 60 else "developing"} alignment</em></p>
</div>
</div>
"""
def create_main_interface():
"""Create the main Gradio interface"""
with gr.Blocks(
css=APPLE_CSS,
title="IQKiller - AI Interview Prep"
) as demo:
# Header
gr.HTML("""
<div class="main-header">
<h1 class="main-title">🎯 IQKiller</h1>
<p style="color: rgba(255, 255, 255, 0.8); font-size: 1.2rem; margin-bottom: 10px;">
AI-Powered Interview Preparation Platform
</p>
<p style="color: rgba(255, 255, 255, 0.6); font-size: 0.9rem;">
πŸ”— URL Scraping β€’ πŸ“‹ Comprehensive Guides β€’ πŸ’Ό Salary Negotiation Training
</p>
</div>
""")
# System Status
gr.HTML(create_status_display())
# Main Interface
with gr.Row():
with gr.Column(scale=1):
gr.HTML("""
<div class="glass-panel">
<h3 style="color: white; margin-bottom: 20px;">πŸ“„ Your Resume</h3>
</div>
""")
resume_input = gr.Textbox(
label="",
placeholder="Paste your resume text here...\n\nInclude your experience, skills, education, and achievements.\n\nExample:\n- 5 years software engineering experience\n- Skills: Python, JavaScript, SQL\n- Led team of 3 developers\n- Built scalable applications\n\nπŸ’‘ Have a PDF resume? Use the pdf_upload_tool.py script to extract text first!",
lines=12,
max_lines=20
)
with gr.Column(scale=1):
gr.HTML("""
<div class="glass-panel">
<h3 style="color: white; margin-bottom: 20px;">πŸ’Ό Job Opportunity</h3>
</div>
""")
job_input = gr.Textbox(
label="",
placeholder="πŸ”— Paste any job URL for automatic scraping:\nβ€’ https://linkedin.com/jobs/view/123456\nβ€’ https://jobs.lever.co/company/role-id\nβ€’ https://apply.workable.com/company/...\n\nπŸ“ Or paste the full job description text:\nβ€’ Company name and role\nβ€’ Required skills and experience \nβ€’ Responsibilities and requirements\n\n✨ URL scraping provides the most comprehensive analysis!",
lines=12,
max_lines=20
)
# Single Action Button
with gr.Row():
guide_btn = gr.Button(
"🎯 Generate My Personalized Interview Guide",
variant="primary",
size="lg"
)
# Results Section
results_output = gr.HTML(label="")
negotiation_output = gr.HTML(label="")
scroll_js = gr.HTML(visible=False)
# Event handler for comprehensive guide generation
guide_btn.click(
fn=lambda r, j: asyncio.run(generate_comprehensive_guide(r, j)),
inputs=[resume_input, job_input],
outputs=[results_output, negotiation_output, scroll_js]
)
# Footer
gr.HTML("""
<div style="text-align: center; margin-top: 40px; color: rgba(255,255,255,0.6);">
<p>🎯 Built for job seekers who want to ace their interviews</p>
<p style="font-size: 0.8rem;">IQKiller v2.0 β€’ URL Scraping β€’ Comprehensive Guides β€’ Zero data retention</p>
</div>
""")
return demo
def main():
"""Main function to launch the IQKiller platform"""
print("🎯 IQKiller - Simplified Complete Platform")
print("=" * 50)
# Check API key status
if not OPENAI_API_KEY:
print("⚠️ OpenAI API key not found - using simplified analysis")
else:
print("βœ… OpenAI API key configured")
if ANTHROPIC_API_KEY:
print("βœ… Anthropic API key configured")
if SERPAPI_KEY:
print("βœ… SerpAPI key configured")
print(f"βœ… URL Scraping: {'Ready' if scraping_available else 'Limited mode'}")
print(f"βœ… Negotiation simulator: {'Ready' if negotiation_available else 'Simplified mode'}")
print(f"βœ… LLM client: {'Ready' if llm_available else 'Simplified mode'}")
print(f"βœ… Comprehensive guides: {'Ready' if comprehensive_available else 'Basic mode'}")
print("\nπŸš€ Starting IQKiller Platform...")
print("🌐 Open your browser to: http://localhost:7860")
print("πŸ’‘ Paste any job URL for automatic scraping and comprehensive analysis!")
print("=" * 50)
# Create and launch
demo = create_main_interface()
try:
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
show_error=True,
quiet=False
)
except Exception as e:
print(f"❌ Failed to launch: {e}")
print("πŸ› οΈ Try using a different port: python3 simple_iqkiller.py")
if __name__ == "__main__":
main()