Requirement_AI / app.py
hari7261's picture
Update app.py
4366208 verified
Raw
History Blame Contribute Delete
35.2 kB
import sys
import warnings
warnings.filterwarnings('ignore')
# Python 3.13 audioop workaround
if sys.version_info >= (3, 13):
try:
import audioop
except ImportError:
import types
audioop = types.ModuleType('audioop')
sys.modules['audioop'] = audioop
for func in ['add', 'adpcm2lin', 'adpcm32lin', 'alaw2lin', 'avg', 'avgpp',
'bias', 'cross', 'findfactor', 'findfit', 'findmax', 'getsample',
'lin2adpcm', 'lin2alaw', 'lin2ulaw', 'max', 'maxpp', 'mul', 'ratecv',
'reverse', 'rms', 'tomono', 'tostereo', 'ulaw2lin']:
setattr(audioop, func, lambda *args, **kwargs: b'')
sys.modules['pyaudioop'] = audioop
# Now import gradio
import gradio as gr
import os
import time
import json
import requests
import PyPDF2
from datetime import datetime, timedelta
import pytz
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from typing import Literal, Tuple, Dict, Optional
import traceback
# Import Agno components
try:
from phi.agent import Agent
from phi.model.google import Gemini
from phi.utils.log import logger
except ImportError:
try:
from agno.agent import Agent
from agno.models.google import Gemini
from agno.utils.log import logger
except ImportError:
# Fallback to dummy logger
import logging
logger = logging.getLogger(__name__)
# Create dummy Agent class if needed
class Agent:
def __init__(self, *args, **kwargs):
pass
class Gemini:
def __init__(self, *args, **kwargs):
pass
class CustomZoomTool:
def __init__(self, *, account_id: Optional[str] = None, client_id: Optional[str] = None,
client_secret: Optional[str] = None, name: str = "zoom_tool"):
self.account_id = account_id
self.client_id = client_id
self.client_secret = client_secret
self.name = name
self.token_url = "https://zoom.us/oauth/token"
self.base_url = "https://api.zoom.us/v2"
self.access_token = None
self.token_expires_at = 0
def get_access_token(self) -> str:
if self.access_token and time.time() < self.token_expires_at:
return str(self.access_token)
if not self.account_id or not self.client_id or not self.client_secret:
logger.error("Missing Zoom credentials")
return ""
headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {"grant_type": "account_credentials", "account_id": self.account_id}
try:
response = requests.post(
self.token_url,
headers=headers,
data=data,
auth=(self.client_id, self.client_secret),
timeout=30
)
if response.status_code != 200:
logger.error(f"Zoom token request failed: {response.status_code} - {response.text}")
return ""
response.raise_for_status()
token_info = response.json()
self.access_token = token_info["access_token"]
expires_in = token_info["expires_in"]
self.token_expires_at = time.time() + expires_in - 60
return str(self.access_token)
except Exception as e:
logger.error(f"Error fetching access token: {e}")
return ""
def create_meeting(self, title: str, start_time: str, duration: int = 60, attendee_email: str = "") -> Dict:
access_token = self.get_access_token()
if not access_token:
return {"error": "Could not get access token. Please check your Zoom credentials."}
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
meeting_data = {
"topic": title,
"type": 2,
"start_time": start_time,
"duration": duration,
"timezone": "Asia/Kolkata",
"settings": {
"host_video": True,
"participant_video": True,
"join_before_host": False,
"mute_upon_entry": True,
"waiting_room": False,
"registrants_email_notification": True
}
}
try:
response = requests.post(
f"{self.base_url}/users/me/meetings",
headers=headers,
json=meeting_data,
timeout=30
)
if response.status_code != 201:
error_text = response.text
if "scopes" in error_text:
return {"error": f"Zoom app missing required scopes. Please add 'meeting:write:meeting' and 'meeting:write:meeting:admin' scopes to your Zoom app."}
return {"error": f"Meeting creation failed: {response.status_code} - {error_text}"}
response.raise_for_status()
meeting_info = response.json()
if attendee_email:
self.add_attendee(meeting_info["id"], attendee_email)
return meeting_info
except Exception as e:
logger.error(f"Error creating meeting: {e}")
return {"error": f"Error creating meeting: {str(e)}"}
def add_attendee(self, meeting_id: str, email: str) -> bool:
access_token = self.get_access_token()
if not access_token:
return False
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
registrant_data = {
"email": email,
"first_name": "Candidate",
"last_name": "Interview"
}
try:
response = requests.post(
f"{self.base_url}/meetings/{meeting_id}/registrants",
headers=headers,
json=registrant_data
)
response.raise_for_status()
return True
except Exception as e:
logger.error(f"Error adding attendee: {e}")
return False
ROLE_REQUIREMENTS: Dict[str, str] = {
"ai_ml_engineer": """
Required Skills:
- Python, PyTorch/TensorFlow
- Machine Learning algorithms and frameworks
- Deep Learning and Neural Networks
- Data preprocessing and analysis
- MLOps and model deployment
- RAG, LLM, Finetuning and Prompt Engineering
""",
"frontend_engineer": """
Required Skills:
- React/Vue.js/Angular
- HTML5, CSS3, JavaScript/TypeScript
- Responsive design
- State management
- Frontend testing
""",
"backend_engineer": """
Required Skills:
- Python/Java/Node.js
- REST APIs
- Database design and management
- System architecture
- Cloud services (AWS/GCP/Azure)
- Kubernetes, Docker, CI/CD
"""
}
class SessionState:
def __init__(self):
self.candidate_email = ""
self.resume_text = ""
self.analysis_complete = False
self.is_selected = False
self.gemini_api_key = os.getenv("GEMINI_API_KEY", "")
self.zoom_account_id = os.getenv("ZOOM_ACCOUNT_ID", "")
self.zoom_client_id = os.getenv("ZOOM_CLIENT_ID", "")
self.zoom_client_secret = os.getenv("ZOOM_CLIENT_SECRET", "")
self.email_sender = os.getenv("EMAIL_SENDER", "")
self.email_passkey = os.getenv("EMAIL_PASSKEY", "")
self.company_name = os.getenv("COMPANY_NAME", "AI Recruiting Team")
session_state = SessionState()
def create_resume_analyzer():
if not session_state.gemini_api_key:
return None
try:
from phi.agent import Agent
from phi.model.google import Gemini
return Agent(
model=Gemini(
id="gemini-2.0-flash",
api_key=session_state.gemini_api_key
),
description="You are an expert technical recruiter who analyzes resumes.",
instructions=[
"Analyze the resume against the provided job requirements",
"Be lenient with AI/ML candidates who show strong potential",
"Consider project experience as valid experience",
"Value hands-on experience with key technologies",
"Return a JSON response with selection decision and feedback"
],
markdown=True
)
except Exception as e:
logger.error(f"Error creating resume analyzer: {e}")
return None
def create_email_agent():
try:
from phi.agent import Agent
from phi.model.google import Gemini
return Agent(
model=Gemini(
id="gemini-1.5-flash",
api_key=session_state.gemini_api_key
),
description="You are a professional recruitment coordinator handling email communications.",
instructions=[
"Draft professional recruitment emails",
"Act like a human writing an email and use all lowercase letters",
"Maintain a friendly yet professional tone",
"Always end emails with exactly: 'best,\nthe ai recruiting team'",
"Never include the sender's or receiver's name in the signature",
f"The name of the company is '{session_state.company_name}'"
],
markdown=True
)
except Exception as e:
logger.error(f"Error creating email agent: {e}")
return None
def create_scheduler_agent():
try:
from phi.agent import Agent
from phi.model.google import Gemini
zoom_tools = CustomZoomTool(
account_id=session_state.zoom_account_id,
client_id=session_state.zoom_client_id,
client_secret=session_state.zoom_client_secret
)
return Agent(
name="Interview Scheduler",
model=Gemini(
id="gemini-1.5-flash",
api_key=session_state.gemini_api_key
),
description="You are an interview scheduling coordinator.",
instructions=[
"You are an expert at scheduling technical interviews using Zoom.",
"Schedule interviews during business hours (9 AM - 5 PM EST)",
"Create meetings with proper titles and descriptions",
"Ensure all meeting details are included in responses",
"Use ISO 8601 format for dates",
"Handle scheduling errors gracefully"
],
markdown=True,
show_tool_calls=True
)
except Exception as e:
logger.error(f"Error creating scheduler agent: {e}")
return None
def send_email(to_email: str, subject: str, body: str) -> tuple[bool, str]:
try:
if not session_state.email_sender or not session_state.email_passkey:
return False, "Email credentials not configured properly"
msg = MIMEMultipart()
msg['From'] = session_state.email_sender
msg['To'] = to_email
msg['Subject'] = subject
msg.attach(MIMEText(body, 'plain'))
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(session_state.email_sender, session_state.email_passkey)
text = msg.as_string()
server.sendmail(session_state.email_sender, to_email, text)
server.quit()
return True, f"Email sent successfully to {to_email}"
except Exception as e:
logger.error(f"Error sending email: {e}")
return False, f"Error sending email: {e}"
def extract_text_from_pdf(pdf_path) -> str:
try:
with open(pdf_path, 'rb') as file:
pdf_reader = PyPDF2.PdfReader(file)
text = ""
for page in pdf_reader.pages:
text += page.extract_text()
return text
except Exception as e:
return f"Error extracting PDF text: {str(e)}"
def analyze_resume(resume_text: str, role: str, analyzer) -> Tuple[bool, str]:
try:
response = analyzer.run(
f"""Please analyze this resume against the following requirements and provide your response in valid JSON format:
Role Requirements:
{ROLE_REQUIREMENTS[role]}
Resume Text:
{resume_text}
Your response must be a valid JSON object like this:
{{
"selected": true,
"feedback": "Detailed feedback explaining the decision",
"matching_skills": ["skill1", "skill2"],
"missing_skills": ["skill3", "skill4"],
"experience_level": "junior/mid/senior"
}}
Evaluation criteria:
1. Match at least 70% of required skills
2. Consider both theoretical knowledge and practical experience
3. Value project experience and real-world applications
4. Consider transferable skills from similar technologies
5. Look for evidence of continuous learning and adaptability
IMPORTANT:
- Return ONLY the JSON object
- Do not include any markdown formatting, backticks, or explanatory text
"""
)
assistant_message = None
if hasattr(response, 'messages'):
for msg in response.messages:
if hasattr(msg, 'role') and msg.role == 'assistant':
assistant_message = msg.content
break
elif hasattr(response, 'content'):
assistant_message = response.content
if not assistant_message:
raise ValueError("No assistant message found in response.")
content = assistant_message.strip()
if content.startswith('```json'):
content = content[7:]
if content.startswith('```'):
content = content[3:]
if content.endswith('```'):
content = content[:-3]
content = content.strip()
result = json.loads(content)
if not isinstance(result, dict) or not all(k in result for k in ["selected", "feedback"]):
raise ValueError("Invalid response format")
return result["selected"], result["feedback"]
except Exception as e:
return False, f"Resume analysis failed: {str(e)}"
def send_selection_email(email_agent, to_email: str, role: str) -> tuple[bool, str]:
try:
response = email_agent.run(
f"""
Draft an email to congratulate a candidate on being selected for the {role} position.
The email should:
1. Congratulate them on being selected
2. Explain the next steps in the process
3. Mention that they will receive interview details shortly
4. The name of the company is 'AI Recruiting Team'
5. Use all lowercase letters
6. End with exactly: 'best,\nthe ai recruiting team'
Return only the email body text, no subject line.
"""
)
email_content = None
if hasattr(response, 'messages'):
for msg in response.messages:
if hasattr(msg, 'role') and msg.role == 'assistant':
email_content = msg.content
break
elif hasattr(response, 'content'):
email_content = response.content
if not email_content:
return False, "Could not generate email content"
return send_email(to_email, f"congratulations! you've been selected for the {role} position", email_content)
except Exception as e:
return False, f"Error sending selection email: {str(e)}"
def send_rejection_email(email_agent, to_email: str, role: str, feedback: str) -> tuple[bool, str]:
try:
response = email_agent.run(
f"""
Draft an email regarding rejection for the {role} position.
Use this specific style:
1. use all lowercase letters
2. be empathetic and human
3. mention specific feedback from: {feedback}
4. encourage them to upskill and try again
5. suggest some learning resources based on missing skills
6. end the email with exactly: best,\nthe ai recruiting team
Do not include any names in the signature.
Return only the email body text, no subject line.
"""
)
email_content = None
if hasattr(response, 'messages'):
for msg in response.messages:
if hasattr(msg, 'role') and msg.role == 'assistant':
email_content = msg.content
break
elif hasattr(response, 'content'):
email_content = response.content
if not email_content:
return False, "Could not generate email content"
return send_email(to_email, f"update on your application for {role} position", email_content)
except Exception as e:
return False, f"Error sending rejection email: {str(e)}"
def schedule_interview(scheduler, candidate_email: str, email_agent, role: str) -> tuple[bool, str]:
try:
ist_tz = pytz.timezone('Asia/Kolkata')
current_time_ist = datetime.now(ist_tz)
tomorrow_ist = current_time_ist + timedelta(days=1)
interview_time = tomorrow_ist.replace(hour=11, minute=0, second=0, microsecond=0)
formatted_time = interview_time.strftime('%Y-%m-%dT%H:%M:%S')
zoom_tool = CustomZoomTool(
account_id=session_state.zoom_account_id,
client_id=session_state.zoom_client_id,
client_secret=session_state.zoom_client_secret
)
meeting_info = zoom_tool.create_meeting(
title=f"{role} Technical Interview",
start_time=formatted_time,
duration=60,
attendee_email=candidate_email
)
if "error" in meeting_info:
meeting_details = f"""
πŸ“… Interview Details:
- Meeting Title: {role} Technical Interview
- Date & Time: {interview_time.strftime('%B %d, %Y at %I:%M %p IST')}
- Duration: 60 minutes
Note: Zoom meeting link will be shared separately by our team.
Please be available at the scheduled time.
Time zone: IST (India Standard Time - UTC+5:30)
"""
else:
meeting_details = f"""
πŸ“… Interview Details:
- Meeting Title: {meeting_info.get('topic', 'Technical Interview')}
- Date & Time: {interview_time.strftime('%B %d, %Y at %I:%M %p IST')}
- Duration: 60 minutes
- Join URL: {meeting_info.get('join_url', 'N/A')}
- Meeting ID: {meeting_info.get('id', 'N/A')}
- Passcode: {meeting_info.get('password', 'N/A')}
Please join the meeting 5 minutes early and be confident!
Time zone: IST (India Standard Time - UTC+5:30)
"""
response = email_agent.run(
f"""Draft an interview confirmation email with these details:
- Role: {role} position
- Meeting Details: {meeting_details}
Important:
- Use all lowercase letters
- Clearly specify that the time is in IST (India Standard Time)
- Ask the candidate to join 5 minutes early
- Ask them to be confident and not nervous
- End with exactly: 'best,\nthe ai recruiting team'
Return only the email body text, no subject line.
"""
)
email_content = None
if hasattr(response, 'messages'):
for msg in response.messages:
if hasattr(msg, 'role') and msg.role == 'assistant':
email_content = msg.content
break
elif hasattr(response, 'content'):
email_content = response.content
if not email_content:
return False, "Could not generate interview email content"
success, message = send_email(
to_email=candidate_email,
subject=f"interview details for {role} position",
body=email_content
)
if success:
return True, f"Interview scheduled successfully! Check your email for details."
else:
return False, f"Failed to send email notification: {message}"
except Exception as e:
return False, f"Unable to schedule interview: {str(e)}"
# Gradio Functions
def process_resume(file, role, email, gemini_key, zoom_account, zoom_client, zoom_secret, email_sender, email_pass, company):
session_state.gemini_api_key = gemini_key or os.getenv("GEMINI_API_KEY", "")
session_state.zoom_account_id = zoom_account or os.getenv("ZOOM_ACCOUNT_ID", "")
session_state.zoom_client_id = zoom_client or os.getenv("ZOOM_CLIENT_ID", "")
session_state.zoom_client_secret = zoom_secret or os.getenv("ZOOM_CLIENT_SECRET", "")
session_state.email_sender = email_sender or os.getenv("EMAIL_SENDER", "")
session_state.email_passkey = email_pass or os.getenv("EMAIL_PASSKEY", "")
session_state.company_name = company or os.getenv("COMPANY_NAME", "AI Recruiting Team")
session_state.candidate_email = email
if not all([session_state.gemini_api_key, session_state.zoom_account_id,
session_state.zoom_client_id, session_state.zoom_client_secret,
session_state.email_sender, session_state.email_passkey]):
return "❌ Please configure all required settings in the Configuration tab first.", None, None, "", ""
if file is None:
return "❌ Please upload your resume PDF.", None, None, "", ""
if not email:
return "❌ Please enter your email address.", None, None, "", ""
try:
resume_text = extract_text_from_pdf(file)
if not resume_text or "Error" in resume_text:
return f"❌ {resume_text}", None, None, "", ""
session_state.resume_text = resume_text
analyzer = create_resume_analyzer()
if analyzer is None:
return "❌ Failed to create resume analyzer. Please check your Gemini API key.", None, None, "", ""
email_agent = create_email_agent()
if email_agent is None:
return "❌ Failed to create email agent.", None, None, "", ""
is_selected, feedback = analyze_resume(resume_text, role, analyzer)
if is_selected:
session_state.analysis_complete = True
session_state.is_selected = True
success, message = send_selection_email(email_agent, email, role)
email_status = f"βœ… Selection email sent!\n{message}" if success else f"⚠️ {message}"
return f"βœ… Congratulations! Your skills match our requirements.\n\nFeedback: {feedback}\n\n{email_status}", "Selected", feedback, "", "βœ… Application approved! Click 'Schedule Interview' to proceed."
else:
success, message = send_rejection_email(email_agent, email, role, feedback)
email_status = f"βœ… Rejection email sent!\n{message}" if success else f"⚠️ {message}"
return f"❌ Unfortunately, your skills don't match our requirements.\n\nFeedback: {feedback}\n\n{email_status}", "Rejected", feedback, "", ""
except Exception as e:
return f"❌ Error processing application: {str(e)}\n\n{traceback.format_exc()}", None, None, "", ""
def schedule_interview_gradio(role, email, gemini_key, zoom_account, zoom_client, zoom_secret, email_sender, email_pass, company):
session_state.gemini_api_key = gemini_key or os.getenv("GEMINI_API_KEY", "")
session_state.zoom_account_id = zoom_account or os.getenv("ZOOM_ACCOUNT_ID", "")
session_state.zoom_client_id = zoom_client or os.getenv("ZOOM_CLIENT_ID", "")
session_state.zoom_client_secret = zoom_secret or os.getenv("ZOOM_CLIENT_SECRET", "")
session_state.email_sender = email_sender or os.getenv("EMAIL_SENDER", "")
session_state.email_passkey = email_pass or os.getenv("EMAIL_PASSKEY", "")
session_state.company_name = company or os.getenv("COMPANY_NAME", "AI Recruiting Team")
session_state.candidate_email = email
if not all([session_state.gemini_api_key, session_state.zoom_account_id,
session_state.zoom_client_id, session_state.zoom_client_secret,
session_state.email_sender, session_state.email_passkey]):
return "❌ Please configure all required settings in the Configuration tab first."
if not email:
return "❌ Please enter your email address."
if not session_state.is_selected:
return "❌ You must be selected first. Please analyze your resume first."
try:
email_agent = create_email_agent()
if email_agent is None:
return "❌ Failed to create email agent."
scheduler_agent = create_scheduler_agent()
if scheduler_agent is None:
return "❌ Failed to create scheduler agent."
success, message = schedule_interview(scheduler_agent, email, email_agent, role)
if success:
return f"βœ… Interview scheduled successfully!\n\n{message}"
else:
return f"⚠️ {message}"
except Exception as e:
return f"❌ Error scheduling interview: {str(e)}\n\n{traceback.format_exc()}"
def reset_application():
session_state.candidate_email = ""
session_state.resume_text = ""
session_state.analysis_complete = False
session_state.is_selected = False
return "πŸ”„ Application reset successfully! You can start a new application.", "", "", "", ""
def test_email_config(email_sender_val, email_pass_val):
if not email_sender_val or not email_pass_val:
return "❌ Please fill in both Email Sender and Email App Password fields."
session_state.email_sender = email_sender_val
session_state.email_passkey = email_pass_val
success, message = send_email(
to_email=email_sender_val,
subject="Test Email from AI Recruitment System",
body="This is a test email to verify your email configuration.\n\nbest,\nthe ai recruiting team"
)
return f"{'βœ…' if success else '❌'} Test Result: {message}"
def test_zoom_config(zoom_account_val, zoom_client_val, zoom_secret_val):
if not all([zoom_account_val, zoom_client_val, zoom_secret_val]):
return "❌ Please fill in all Zoom configuration fields."
zoom_test = CustomZoomTool(
account_id=zoom_account_val,
client_id=zoom_client_val,
client_secret=zoom_secret_val
)
token = zoom_test.get_access_token()
if token:
return "βœ… Zoom authentication successful!"
else:
return "❌ Zoom authentication failed! Check your credentials."
def update_role_requirements(role_choice):
return ROLE_REQUIREMENTS[role_choice]
# Create Gradio UI
with gr.Blocks(title="AI Recruitment System", theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# 🎯 AI Recruitment System
Upload your resume and get instant feedback! Our AI analyzes your skills against job requirements.
""")
with gr.Tabs():
with gr.TabItem("πŸ“ Application"):
with gr.Row():
with gr.Column(scale=2):
role = gr.Dropdown(
choices=["ai_ml_engineer", "frontend_engineer", "backend_engineer"],
label="Select Role",
value="ai_ml_engineer"
)
resume_file = gr.File(
label="Upload Resume (PDF)",
file_types=[".pdf"],
type="filepath"
)
email = gr.Textbox(
label="Candidate's Email Address",
placeholder="Enter your email address",
type="text"
)
with gr.Row():
analyze_btn = gr.Button("πŸ” Analyze Resume", variant="primary", size="lg")
reset_btn = gr.Button("πŸ”„ Reset Application", variant="secondary", size="lg")
status_output = gr.Textbox(
label="Status",
lines=10,
interactive=False,
value="Ready to process your application..."
)
with gr.Column(scale=1):
gr.Markdown("### πŸ“‹ Role Requirements")
role_requirements = gr.Markdown(ROLE_REQUIREMENTS["ai_ml_engineer"])
result_status = gr.Textbox(label="Application Result", lines=2, interactive=False, visible=True)
feedback_output = gr.Textbox(label="Feedback Details", lines=5, interactive=False, visible=True)
interview_status_indicator = gr.Textbox(label="Interview Status", lines=3, interactive=False, value="", visible=True)
with gr.Row():
with gr.Column():
gr.Markdown("### πŸ“… Interview Scheduling")
schedule_btn = gr.Button("πŸ“… Schedule Interview", variant="primary", size="lg")
interview_status = gr.Textbox(
label="Interview Status",
lines=6,
interactive=False,
value="Schedule your interview after resume analysis..."
)
with gr.TabItem("βš™οΈ Configuration"):
gr.Markdown("""
### πŸ”‘ API Configuration
**Note:** You can either enter credentials here or set them as Hugging Face Space secrets.
Secrets will be automatically used if fields are left empty.
""")
gemini_key = gr.Textbox(
label="Gemini API Key",
type="password",
placeholder="Enter your Gemini API key (or set as GEMINI_API_KEY secret)",
value=os.getenv("GEMINI_API_KEY", "")
)
with gr.Row():
zoom_account = gr.Textbox(
label="Zoom Account ID",
type="password",
placeholder="Zoom Account ID (or set as ZOOM_ACCOUNT_ID secret)",
value=os.getenv("ZOOM_ACCOUNT_ID", "")
)
zoom_client = gr.Textbox(
label="Zoom Client ID",
type="password",
placeholder="Zoom Client ID (or set as ZOOM_CLIENT_ID secret)",
value=os.getenv("ZOOM_CLIENT_ID", "")
)
zoom_secret = gr.Textbox(
label="Zoom Client Secret",
type="password",
placeholder="Zoom Client Secret (or set as ZOOM_CLIENT_SECRET secret)",
value=os.getenv("ZOOM_CLIENT_SECRET", "")
)
with gr.Row():
email_sender = gr.Textbox(
label="Sender Email",
placeholder="your-email@gmail.com (or set as EMAIL_SENDER secret)",
value=os.getenv("EMAIL_SENDER", "")
)
email_pass = gr.Textbox(
label="Email App Password",
type="password",
placeholder="16-character app password (or set as EMAIL_PASSKEY secret)",
value=os.getenv("EMAIL_PASSKEY", "")
)
company = gr.Textbox(
label="Company Name",
placeholder="Your Company Name",
value=os.getenv("COMPANY_NAME", "AI Recruiting Team")
)
gr.Markdown("### πŸ§ͺ Test Configuration")
with gr.Row():
test_email_btn = gr.Button("πŸ“§ Test Email", variant="secondary")
test_zoom_btn = gr.Button("πŸ”— Test Zoom", variant="secondary")
test_output = gr.Textbox(
label="Test Results",
lines=4,
interactive=False,
value="Click a test button to verify your configuration..."
)
with gr.TabItem("πŸ“– Help"):
gr.Markdown("""
## πŸ“‹ How to Use the AI Recruitment System
### Step 1: Configure Settings
1. Go to the **Configuration** tab
2. Enter your **Gemini API Key** from [Google AI Studio](https://aistudio.google.com)
3. Enter your **Zoom credentials** (Account ID, Client ID, Client Secret)
4. Enter your **Email settings** (Gmail address and App Password)
5. Click **Test Email** and **Test Zoom** to verify your configuration
### Step 2: Submit Application
1. Go to the **Application** tab
2. Select the **Role** you're applying for
3. Upload your **Resume** (PDF format)
4. Enter your **Email Address**
5. Click **Analyze Resume**
### Step 3: Review Results
- If selected: You'll receive a confirmation email and can schedule an interview
- If rejected: You'll receive constructive feedback via email
### Step 4: Schedule Interview (if selected)
1. Click **Schedule Interview**
2. Check your email for the Zoom meeting details
3. Join the interview 5 minutes early
""")
# Event handlers
role.change(fn=update_role_requirements, inputs=role, outputs=role_requirements)
analyze_btn.click(
fn=process_resume,
inputs=[resume_file, role, email, gemini_key, zoom_account, zoom_client,
zoom_secret, email_sender, email_pass, company],
outputs=[status_output, result_status, feedback_output, interview_status_indicator, status_output]
)
schedule_btn.click(
fn=schedule_interview_gradio,
inputs=[role, email, gemini_key, zoom_account, zoom_client,
zoom_secret, email_sender, email_pass, company],
outputs=interview_status
)
reset_btn.click(
fn=reset_application,
inputs=[],
outputs=[status_output, result_status, feedback_output, interview_status_indicator, interview_status]
)
test_email_btn.click(
fn=test_email_config,
inputs=[email_sender, email_pass],
outputs=test_output
)
test_zoom_btn.click(
fn=test_zoom_config,
inputs=[zoom_account, zoom_client, zoom_secret],
outputs=test_output
)
if __name__ == "__main__":
demo.launch(share=True)