RubaKhan242 commited on
Commit
88e1038
Β·
verified Β·
1 Parent(s): a303325

Upload 2 files

Browse files
Files changed (2) hide show
  1. main.py +177 -0
  2. requirements.txt +7 -3
main.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pdfplumber
3
+ import json
4
+ import os
5
+ import smtplib
6
+ from email.message import EmailMessage
7
+ from langchain_google_genai import GoogleGenerativeAI
8
+ from langchain.prompts import PromptTemplate
9
+ from langchain.chains import LLMChain
10
+ from dotenv import load_dotenv
11
+ import re
12
+
13
+ # Load environment variables
14
+ load_dotenv()
15
+ GOOGLE_API_KEY = os.getenv("GEMINI_API_KEY")
16
+ EMAIL_USER = os.getenv("EMAIL_USER")
17
+ EMAIL_PASS = os.getenv("EMAIL_PASS")
18
+ HR_EMAIL = os.getenv("HR_EMAIL") # HR Email Address
19
+
20
+ # Initialize LLM
21
+ llm = GoogleGenerativeAI(
22
+ model="gemini-1.5-flash",
23
+ google_api_key=GOOGLE_API_KEY,
24
+ temperature=0.7
25
+ )
26
+
27
+ # Extract text from PDF
28
+ def extract_text_from_pdf(uploaded_file):
29
+ with pdfplumber.open(uploaded_file) as pdf:
30
+ text = "\n".join([page.extract_text() for page in pdf.pages if page.extract_text()])
31
+ return text
32
+
33
+ # Prompt for Resume Screening
34
+ prompt = PromptTemplate(
35
+ input_variables=["resume_text", "job_description"],
36
+ template="""
37
+ You are an expert resume screener. Extract key details from the following resume text:
38
+
39
+ Resume:
40
+ {resume_text}
41
+
42
+ Compare it with the given job description:
43
+ {job_description}
44
+
45
+ Provide a structured JSON response with skills, experience, education, score, and missing skills.
46
+ """
47
+ )
48
+
49
+ # Resume Screening Chain
50
+ resume_screener_chain = LLMChain(llm=llm, prompt=prompt)
51
+
52
+ def calculate_similarity(resume_text, job_desc):
53
+ response = resume_screener_chain.invoke({"resume_text": resume_text, "job_description": job_desc})
54
+ response_text = response.get("text", "{}")
55
+ response_text = response_text.replace("```json", "").replace("```", "").strip()
56
+ try:
57
+ return json.loads(response_text)
58
+ except json.JSONDecodeError:
59
+ return {"score": 0, "missing_skills": []}
60
+
61
+ # Email Function (Updated with Debugging & Validation)
62
+ def is_valid_email(email):
63
+ return re.match(r"^[\w\.-]+@[\w\.-]+\.[a-zA-Z]{2,}$", email)
64
+
65
+ def send_email(to_email, subject, body, attachment=None, attachment_name=None):
66
+ if not is_valid_email(to_email):
67
+ print("❌ Invalid email format")
68
+ return False
69
+
70
+ msg = EmailMessage()
71
+ msg.set_content(body)
72
+ msg["Subject"] = subject
73
+ msg["From"] = EMAIL_USER
74
+ msg["To"] = to_email
75
+
76
+ if attachment and attachment_name:
77
+ msg.add_attachment(attachment, maintype='application', subtype='pdf', filename=attachment_name)
78
+
79
+ try:
80
+ with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
81
+ server.login(EMAIL_USER, EMAIL_PASS)
82
+ server.send_message(msg)
83
+ return True
84
+ except Exception as e:
85
+ print(f"❌ Email Error: {e}")
86
+ return False
87
+
88
+ # Function to Notify HR
89
+ def notify_hr(candidate_name, resume_file):
90
+ hr_subject = f"πŸŽ‰ {candidate_name} shortlisted for Interview"
91
+ hr_body = f"The candidate {candidate_name} has been shortlisted for an interview. Please review their application."
92
+ resume_bytes = resume_file.getvalue()
93
+ notify_success = send_email(HR_EMAIL, hr_subject, hr_body, attachment=resume_bytes, attachment_name=f"{candidate_name}_Resume.pdf")
94
+ return notify_success
95
+
96
+
97
+
98
+ # Streamlit UI (Enhanced)
99
+ st.set_page_config(page_title="Resume Screening System", page_icon="πŸ“„", layout="centered")
100
+
101
+ st.markdown(
102
+ """
103
+ <style>
104
+ .main {
105
+ background-color: #f9f9f9;
106
+ }
107
+ .stButton>button {
108
+ background-color: #4CAF50;
109
+ color: white;
110
+ font-weight: bold;
111
+ border-radius: 8px;
112
+ padding: 10px 24px;
113
+ }
114
+ .stTextInput>div>div>input {
115
+ border-radius: 8px;
116
+ }
117
+ </style>
118
+ """, unsafe_allow_html=True
119
+ )
120
+
121
+ st.title("πŸ“„ Smart Resume Screening System")
122
+ st.markdown("Welcome! Upload your resume to see if you're a match for our job opening.")
123
+
124
+ st.markdown("---")
125
+
126
+ # Input Fields in Columns
127
+ col1, col2 = st.columns(2)
128
+ with col1:
129
+ name = st.text_input("πŸ‘€ Candidate Name")
130
+ with col2:
131
+ email = st.text_input("πŸ“§ Candidate Email")
132
+
133
+ resume_file = st.file_uploader("πŸ“Ž Upload Your Resume (PDF Only)", type=["pdf"])
134
+
135
+ st.markdown("---")
136
+
137
+ if st.button("πŸš€ Submit for Screening"):
138
+ if name and email and resume_file:
139
+ st.info("⏳ Screening your resume... please wait.")
140
+ resume_text = extract_text_from_pdf(resume_file)
141
+ job_description = "Looking for a Python Developer with AI expertise."
142
+ response_data = calculate_similarity(resume_text, job_description)
143
+ match_score = response_data.get("score", 0)
144
+ missing_skills = response_data.get("missing_skills", [])
145
+
146
+ st.markdown("### πŸ“Š Screening Result")
147
+ st.progress(int(match_score))
148
+ st.write(f"**Match Score:** `{match_score}`")
149
+ st.write(f"**Missing Skills:** {', '.join(missing_skills) if missing_skills else 'βœ… None'}")
150
+
151
+ if match_score > 80:
152
+ decision = "πŸŽ‰ Congratulations! You have been shortlisted for an interview."
153
+ subject = "Interview Invitation"
154
+ notify_hr(name, resume_file)
155
+ elif 50 <= match_score <= 79:
156
+ decision = "βœ… You have been shortlisted for future opportunities."
157
+ subject = "Shortlist Notification"
158
+ else:
159
+ decision = "❌ Thank you for applying. We’ve decided to move forward with other candidates."
160
+ subject = "Rejection Email"
161
+
162
+ # AI Feedback
163
+ feedback_prompt = f"Suggest skills to improve for this candidate based on missing skills: {missing_skills}"
164
+ feedback_response = llm.invoke(feedback_prompt)
165
+ feedback = feedback_response.get("text", "No feedback available.") if isinstance(feedback_response, dict) else feedback_response
166
+ decision += f"\n\n🧠 **AI Feedback:**\n{feedback}"
167
+
168
+ st.markdown("---")
169
+ st.markdown(f"### βœ‰οΈ Decision Email Content")
170
+ st.success(decision)
171
+
172
+ if send_email(email, subject, decision):
173
+ st.success(f"πŸ“§ Email successfully sent to `{email}`")
174
+ else:
175
+ st.error("❌ Failed to send email. Please check your credentials and try again.")
176
+ else:
177
+ st.warning("⚠️ Please fill in all the fields and upload your resume.")
requirements.txt CHANGED
@@ -1,3 +1,7 @@
1
- altair
2
- pandas
3
- streamlit
 
 
 
 
 
1
+ streamlit
2
+ langchain
3
+ langchain-google-genai
4
+ google-generativeai
5
+ pdfplumber
6
+ python-dotenv
7
+