|
|
import streamlit as st |
|
|
import requests |
|
|
import json |
|
|
import os |
|
|
|
|
|
|
|
|
|
|
|
BACKEND_URL = "http://127.0.0.1:8000/analyze" |
|
|
|
|
|
st.set_page_config(page_title="Smart ATS", page_icon="π―", layout="wide") |
|
|
|
|
|
|
|
|
st.markdown(""" |
|
|
<style> |
|
|
.metric-card { |
|
|
background-color: #f0f2f6; |
|
|
padding: 20px; |
|
|
border-radius: 10px; |
|
|
border-left: 5px solid #4CAF50; |
|
|
} |
|
|
.skill-tag { |
|
|
display: inline-block; |
|
|
padding: 5px 10px; |
|
|
margin: 2px; |
|
|
border-radius: 12px; |
|
|
font-size: 12px; |
|
|
font-weight: bold; |
|
|
} |
|
|
.match { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; } |
|
|
.missing { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } |
|
|
</style> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.title("π― AI Smart Resume Screen") |
|
|
st.markdown("Compare resumes against job descriptions to find the perfect fit.") |
|
|
|
|
|
|
|
|
col1, col2 = st.columns([1, 1]) |
|
|
|
|
|
with col1: |
|
|
st.subheader("1. Job Description (Optional)") |
|
|
jd_input = st.text_area("Paste the Job Description here...", height=200, |
|
|
placeholder="e.g. Seeking a Senior Python Developer with AWS experience...") |
|
|
|
|
|
with col2: |
|
|
st.subheader("2. Candidate Resume") |
|
|
uploaded_file = st.file_uploader("Upload PDF", type="pdf") |
|
|
|
|
|
|
|
|
if st.button("Analyze Fit", type="primary"): |
|
|
if not uploaded_file: |
|
|
st.warning("Please upload a resume first.") |
|
|
else: |
|
|
with st.spinner("Analyzing candidate against requirements..."): |
|
|
try: |
|
|
|
|
|
files = {"file": (uploaded_file.name, uploaded_file.getvalue(), "application/pdf")} |
|
|
data = {"job_description": jd_input} if jd_input else {} |
|
|
|
|
|
response = requests.post(BACKEND_URL, files=files, data=data, timeout=60) |
|
|
|
|
|
if response.status_code == 200: |
|
|
result = response.json() |
|
|
|
|
|
if "error" in result: |
|
|
st.error(result["error"]) |
|
|
else: |
|
|
|
|
|
candidate = result.get("candidate", {}) |
|
|
analysis = result.get("match_analysis", {}) |
|
|
|
|
|
|
|
|
st.divider() |
|
|
c1, c2, c3, c4 = st.columns(4) |
|
|
c1.markdown(f"**Name:** {candidate.get('name', 'N/A')}") |
|
|
c2.markdown(f"**Email:** {candidate.get('email', 'N/A')}") |
|
|
c3.markdown(f"**Phone:** {candidate.get('phone', 'N/A')}") |
|
|
|
|
|
|
|
|
if jd_input: |
|
|
score = analysis.get("score", 0) |
|
|
verdict = analysis.get("verdict", "N/A") |
|
|
|
|
|
|
|
|
score_color = "green" if score >= 80 else "orange" if score >= 50 else "red" |
|
|
|
|
|
st.markdown(f""" |
|
|
<div class="metric-card"> |
|
|
<h3 style="margin:0">Match Score: <span style="color:{score_color}">{score}%</span></h3> |
|
|
<p style="margin:0"><strong>Verdict:</strong> {verdict}</p> |
|
|
<p style="margin-top:10px"><em>"{analysis.get('reasoning', '')}"</em></p> |
|
|
</div> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown("### π§© Skill Gap Analysis") |
|
|
k1, k2 = st.columns(2) |
|
|
|
|
|
with k1: |
|
|
st.success("β
Matching Skills") |
|
|
matches = analysis.get("matching_skills", []) |
|
|
if matches: |
|
|
html = "".join([f'<span class="skill-tag match">{s}</span>' for s in matches]) |
|
|
st.markdown(html, unsafe_allow_html=True) |
|
|
else: |
|
|
st.write("No direct skill matches found.") |
|
|
|
|
|
with k2: |
|
|
st.error("β οΈ Missing / To Improve") |
|
|
missing = analysis.get("missing_skills", []) |
|
|
if missing: |
|
|
html = "".join([f'<span class="skill-tag missing">{s}</span>' for s in missing]) |
|
|
st.markdown(html, unsafe_allow_html=True) |
|
|
else: |
|
|
st.write("No major skills missing!") |
|
|
|
|
|
else: |
|
|
|
|
|
st.info("π‘ Paste a Job Description on the left to see a Match Score!") |
|
|
st.markdown("### π Skills Detected") |
|
|
st.write(", ".join(candidate.get("skills", []))) |
|
|
|
|
|
with st.expander("View Raw JSON Data"): |
|
|
st.json(result) |
|
|
|
|
|
else: |
|
|
st.error(f"Server Error: {response.text}") |
|
|
|
|
|
except Exception as e: |
|
|
st.error(f"Connection Error: {e}") |