LovnishVerma commited on
Commit
f747c18
Β·
verified Β·
1 Parent(s): 8f32986

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +112 -71
app.py CHANGED
@@ -1,88 +1,129 @@
1
  import streamlit as st
2
  import requests
 
3
  import os
4
- from dotenv import load_dotenv
5
 
6
- load_dotenv()
 
 
7
 
8
- # Configuration
9
- # Defaults to localhost for dev, but can be overridden in production (e.g., Docker)
10
- BACKEND_URL = os.getenv("BACKEND_URL", "http://127.0.0.1:8000/process-resume")
11
 
12
- st.set_page_config(page_title="AI Resume Analyzer", page_icon="πŸ“„", layout="centered")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- st.title("πŸ“„ Intelligent Resume Parser")
15
- st.markdown("---")
16
- st.write("Upload a professional resume in PDF format to extract key insights using AI.")
17
 
18
- # Sidebar for status
19
- with st.sidebar:
20
- st.info(f"Connected to Backend: `{BACKEND_URL}`")
21
 
22
- uploaded_file = st.file_uploader("Upload PDF Resume", type="pdf")
 
 
 
23
 
24
- if uploaded_file:
25
- # Basic Frontend Validation
26
- if uploaded_file.size > 5 * 1024 * 1024:
27
- st.error("File is too large! Please upload a file smaller than 5MB.")
 
 
 
 
28
  else:
29
- if st.button("Analyze Resume", type="primary"):
30
- with st.spinner("Processing with AI..."):
31
- try:
32
- files = {
33
- "file": (uploaded_file.name, uploaded_file.getvalue(), "application/pdf")
34
- }
 
 
 
 
35
 
36
- # Set a timeout to prevent hanging
37
- response = requests.post(BACKEND_URL, files=files, timeout=30)
38
-
39
- if response.status_code == 200:
40
- data = response.json()
 
41
 
42
- # Handle case where AI returns an error key
43
- if "error" in data:
44
- st.error(data["error"])
45
- else:
46
- st.success("Extraction Complete!")
 
 
 
 
 
 
47
 
48
- # Summary Section
49
- st.markdown("### πŸ“ Professional Summary")
50
- st.info(data.get('summary', 'No summary available.'))
51
-
52
- # Contact Info
53
- st.markdown("### πŸ“‡ Contact Details")
54
- c1, c2, c3 = st.columns(3)
55
- c1.metric("Name", data.get('name', 'N/A'))
56
- c2.metric("Email", data.get('email', 'N/A'))
57
- c3.metric("Phone", data.get('phone', 'N/A'))
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- # Skills Section
60
- st.markdown("### πŸ›  Technical Skills")
61
- skills = data.get('skills', [])
62
- if skills and isinstance(skills, list):
63
- # CSS styling for tags
64
- st.markdown(
65
- f"""
66
- <div style="display: flex; flex-wrap: wrap; gap: 10px;">
67
- {''.join([f'<span style="background-color: #e0f2f1; color: #00695c; padding: 5px 10px; border-radius: 15px; font-size: 14px;">{skill}</span>' for skill in skills])}
68
- </div>
69
- """,
70
- unsafe_allow_html=True
71
- )
72
- else:
73
- st.write("No specific skills detected.")
74
 
75
- with st.expander("View Raw JSON Data"):
76
- st.json(data)
77
-
78
- elif response.status_code == 413:
79
- st.error("The file is too large for the server to process.")
80
- else:
81
- st.error(f"Server Error: {response.status_code} - {response.text}")
82
 
83
- except requests.exceptions.ConnectionError:
84
- st.error("🚨 Connection Failed: Could not reach the backend server.")
85
- except requests.exceptions.Timeout:
86
- st.error("🚨 Request Timed Out: The AI took too long to respond.")
87
- except Exception as e:
88
- st.error(f"An unexpected error occurred: {e}")
 
1
  import streamlit as st
2
  import requests
3
+ import json
4
  import os
 
5
 
6
+ # CONFIG
7
+ # For Hugging Face Spaces (Same container)
8
+ BACKEND_URL = "http://127.0.0.1:8000/analyze"
9
 
10
+ st.set_page_config(page_title="Smart ATS", page_icon="🎯", layout="wide")
 
 
11
 
12
+ # CUSTOM CSS
13
+ st.markdown("""
14
+ <style>
15
+ .metric-card {
16
+ background-color: #f0f2f6;
17
+ padding: 20px;
18
+ border-radius: 10px;
19
+ border-left: 5px solid #4CAF50;
20
+ }
21
+ .skill-tag {
22
+ display: inline-block;
23
+ padding: 5px 10px;
24
+ margin: 2px;
25
+ border-radius: 12px;
26
+ font-size: 12px;
27
+ font-weight: bold;
28
+ }
29
+ .match { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
30
+ .missing { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
31
+ </style>
32
+ """, unsafe_allow_html=True)
33
 
34
+ st.title("🎯 AI Smart Resume Screen")
35
+ st.markdown("Compare resumes against job descriptions to find the perfect fit.")
 
36
 
37
+ # LAYOUT: Two columns for input
38
+ col1, col2 = st.columns([1, 1])
 
39
 
40
+ with col1:
41
+ st.subheader("1. Job Description (Optional)")
42
+ jd_input = st.text_area("Paste the Job Description here...", height=200,
43
+ placeholder="e.g. Seeking a Senior Python Developer with AWS experience...")
44
 
45
+ with col2:
46
+ st.subheader("2. Candidate Resume")
47
+ uploaded_file = st.file_uploader("Upload PDF", type="pdf")
48
+
49
+ # ACTION
50
+ if st.button("Analyze Fit", type="primary"):
51
+ if not uploaded_file:
52
+ st.warning("Please upload a resume first.")
53
  else:
54
+ with st.spinner("Analyzing candidate against requirements..."):
55
+ try:
56
+ # Prepare Payload
57
+ files = {"file": (uploaded_file.name, uploaded_file.getvalue(), "application/pdf")}
58
+ data = {"job_description": jd_input} if jd_input else {}
59
+
60
+ response = requests.post(BACKEND_URL, files=files, data=data, timeout=60)
61
+
62
+ if response.status_code == 200:
63
+ result = response.json()
64
 
65
+ if "error" in result:
66
+ st.error(result["error"])
67
+ else:
68
+ # --- DASHBOARD UI ---
69
+ candidate = result.get("candidate", {})
70
+ analysis = result.get("match_analysis", {})
71
 
72
+ # 1. Header (Candidate Info)
73
+ st.divider()
74
+ c1, c2, c3, c4 = st.columns(4)
75
+ c1.markdown(f"**Name:** {candidate.get('name', 'N/A')}")
76
+ c2.markdown(f"**Email:** {candidate.get('email', 'N/A')}")
77
+ c3.markdown(f"**Phone:** {candidate.get('phone', 'N/A')}")
78
+
79
+ # 2. Score Section (Only if JD provided)
80
+ if jd_input:
81
+ score = analysis.get("score", 0)
82
+ verdict = analysis.get("verdict", "N/A")
83
 
84
+ # Color coding the score
85
+ score_color = "green" if score >= 80 else "orange" if score >= 50 else "red"
86
+
87
+ st.markdown(f"""
88
+ <div class="metric-card">
89
+ <h3 style="margin:0">Match Score: <span style="color:{score_color}">{score}%</span></h3>
90
+ <p style="margin:0"><strong>Verdict:</strong> {verdict}</p>
91
+ <p style="margin-top:10px"><em>"{analysis.get('reasoning', '')}"</em></p>
92
+ </div>
93
+ """, unsafe_allow_html=True)
94
+
95
+ st.markdown("### 🧩 Skill Gap Analysis")
96
+ k1, k2 = st.columns(2)
97
+
98
+ with k1:
99
+ st.success("βœ… Matching Skills")
100
+ matches = analysis.get("matching_skills", [])
101
+ if matches:
102
+ html = "".join([f'<span class="skill-tag match">{s}</span>' for s in matches])
103
+ st.markdown(html, unsafe_allow_html=True)
104
+ else:
105
+ st.write("No direct skill matches found.")
106
 
107
+ with k2:
108
+ st.error("⚠️ Missing / To Improve")
109
+ missing = analysis.get("missing_skills", [])
110
+ if missing:
111
+ html = "".join([f'<span class="skill-tag missing">{s}</span>' for s in missing])
112
+ st.markdown(html, unsafe_allow_html=True)
113
+ else:
114
+ st.write("No major skills missing!")
115
+
116
+ else:
117
+ # Standard Extraction View (No JD)
118
+ st.info("πŸ’‘ Paste a Job Description on the left to see a Match Score!")
119
+ st.markdown("### πŸ›  Skills Detected")
120
+ st.write(", ".join(candidate.get("skills", [])))
 
121
 
122
+ with st.expander("View Raw JSON Data"):
123
+ st.json(result)
 
 
 
 
 
124
 
125
+ else:
126
+ st.error(f"Server Error: {response.text}")
127
+
128
+ except Exception as e:
129
+ st.error(f"Connection Error: {e}")