translators-will commited on
Commit
5f8763a
·
verified ·
1 Parent(s): 57c6281

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +261 -0
app.py ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import plotly.express as px
4
+ from datetime import datetime
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ import logging
9
+ from typing import List, Dict, Any
10
+ from openai import OpenAI
11
+ from dotenv import load_dotenv
12
+ from llm_job_assistant import LLMJobAssistant # Our previous class
13
+
14
+ class JobAssistantUI:
15
+ def __init__(self):
16
+ self.setup_streamlit()
17
+ self.load_dotenv()
18
+ self.assistant = LLMJobAssistant()
19
+
20
+ def setup_streamlit(self):
21
+ """Configure Streamlit page settings"""
22
+ st.set_page_config(
23
+ page_title="AI Job Search Assistant",
24
+ page_icon="🔍",
25
+ layout="wide",
26
+ initial_sidebar_state="expanded"
27
+ )
28
+
29
+ def load_dotenv(self):
30
+ """Load environment variables"""
31
+ load_dotenv()
32
+ if not os.getenv('OPENAI_API_KEY'):
33
+ st.sidebar.error("OpenAI API key not found. Please set it in .env file")
34
+
35
+ def render_sidebar(self):
36
+ """Render sidebar controls"""
37
+ with st.sidebar:
38
+ st.title("Search Settings")
39
+
40
+ # Job Search Settings
41
+ st.subheader("Job Search Criteria")
42
+ keywords = st.text_area(
43
+ "Search Keywords (one per line)",
44
+ value="\n".join(self.assistant.config['keywords'])
45
+ )
46
+ self.assistant.config['keywords'] = [k.strip() for k in keywords.split("\n") if k.strip()]
47
+
48
+ # Location Settings
49
+ location_type = st.radio(
50
+ "Location Type",
51
+ ["Remote Only", "Hybrid", "All Locations"]
52
+ )
53
+
54
+ # Experience Level
55
+ experience_level = st.multiselect(
56
+ "Experience Level",
57
+ ["Entry Level", "Mid Level", "Senior", "Lead"],
58
+ default=["Entry Level", "Mid Level"]
59
+ )
60
+
61
+ # Salary Range
62
+ min_salary = st.slider(
63
+ "Minimum Salary (USD)",
64
+ 0, 200000, self.assistant.config['minimum_salary'],
65
+ step=5000
66
+ )
67
+
68
+ # Save Settings
69
+ if st.button("Save Settings"):
70
+ self.assistant.config['minimum_salary'] = min_salary
71
+ self.assistant.save_config()
72
+ st.success("Settings saved!")
73
+
74
+ def render_job_search_tab(self):
75
+ """Render job search tab"""
76
+ st.header("Job Search")
77
+
78
+ col1, col2 = st.columns([2, 1])
79
+
80
+ with col1:
81
+ if st.button("Start New Job Search", type="primary"):
82
+ with st.spinner("Searching for jobs..."):
83
+ jobs_df = self.assistant.run_enhanced_job_search()
84
+ st.session_state['jobs_df'] = jobs_df
85
+ st.success(f"Found {len(jobs_df)} matching jobs!")
86
+
87
+ with col2:
88
+ if st.button("Load Previous Results"):
89
+ try:
90
+ jobs_df = pd.read_pickle('enhanced_jobs.pkl')
91
+ st.session_state['jobs_df'] = jobs_df
92
+ st.success("Previous results loaded!")
93
+ except FileNotFoundError:
94
+ st.error("No previous results found")
95
+
96
+ if 'jobs_df' in st.session_state:
97
+ self.display_job_results(st.session_state['jobs_df'])
98
+
99
+ def display_job_results(self, df: pd.DataFrame):
100
+ """Display job search results"""
101
+ st.subheader("Search Results")
102
+
103
+ # Filters
104
+ col1, col2, col3 = st.columns(3)
105
+ with col1:
106
+ companies = st.multiselect(
107
+ "Filter by Company",
108
+ options=sorted(df['company'].unique())
109
+ )
110
+ with col2:
111
+ min_match = st.slider(
112
+ "Minimum Match Score",
113
+ 0, 100, 50
114
+ )
115
+ with col3:
116
+ sort_by = st.selectbox(
117
+ "Sort by",
118
+ ["Match Score", "Company", "Date Posted"]
119
+ )
120
+
121
+ # Filter DataFrame
122
+ filtered_df = df.copy()
123
+ if companies:
124
+ filtered_df = filtered_df[filtered_df['company'].isin(companies)]
125
+ filtered_df = filtered_df[filtered_df['analysis.match_score'] >= min_match]
126
+
127
+ # Sort DataFrame
128
+ if sort_by == "Match Score":
129
+ filtered_df = filtered_df.sort_values('analysis.match_score', ascending=False)
130
+ elif sort_by == "Company":
131
+ filtered_df = filtered_df.sort_values('company')
132
+ else:
133
+ filtered_df = filtered_df.sort_values('date_scraped', ascending=False)
134
+
135
+ # Display results
136
+ for _, job in filtered_df.iterrows():
137
+ with st.expander(f"{job['title']} at {job['company']} - Match: {job['analysis']['match_score']}%"):
138
+ col1, col2 = st.columns([2, 1])
139
+
140
+ with col1:
141
+ st.write("**Job Description:**")
142
+ st.write(job['full_description'])
143
+
144
+ st.write("**Required Skills:**")
145
+ for skill in job['analysis']['required_skills']:
146
+ st.markdown(f"- {skill}")
147
+
148
+ with col2:
149
+ st.write("**Salary Range:**")
150
+ st.write(job['analysis']['estimated_salary_range'])
151
+
152
+ st.write("**Experience Required:**")
153
+ st.write(job['analysis']['required_experience'])
154
+
155
+ if st.button("Generate Application Materials", key=job['url']):
156
+ with st.spinner("Generating materials..."):
157
+ cover_letter = self.assistant.generate_custom_cover_letter(
158
+ job['analysis'],
159
+ job['company']
160
+ )
161
+ resume_suggestions = self.assistant.tailor_resume(job['analysis'])
162
+
163
+ st.download_button(
164
+ "Download Cover Letter",
165
+ cover_letter,
166
+ file_name=f"cover_letter_{job['company']}.txt"
167
+ )
168
+
169
+ st.download_button(
170
+ "Download Resume Suggestions",
171
+ resume_suggestions,
172
+ file_name=f"resume_suggestions_{job['company']}.txt"
173
+ )
174
+
175
+ def render_analytics_tab(self):
176
+ """Render analytics tab"""
177
+ st.header("Job Search Analytics")
178
+
179
+ if 'jobs_df' in st.session_state:
180
+ df = st.session_state['jobs_df']
181
+
182
+ col1, col2 = st.columns(2)
183
+
184
+ with col1:
185
+ # Match Score Distribution
186
+ fig = px.histogram(
187
+ df,
188
+ x='analysis.match_score',
189
+ title='Distribution of Match Scores',
190
+ labels={'analysis.match_score': 'Match Score'}
191
+ )
192
+ st.plotly_chart(fig)
193
+
194
+ with col2:
195
+ # Company Distribution
196
+ company_counts = df['company'].value_counts().head(10)
197
+ fig = px.bar(
198
+ company_counts,
199
+ title='Top Companies',
200
+ labels={'value': 'Number of Jobs', 'index': 'Company'}
201
+ )
202
+ st.plotly_chart(fig)
203
+
204
+ # Salary Distribution
205
+ fig = px.box(
206
+ df,
207
+ y='analysis.estimated_salary_range',
208
+ title='Salary Distribution'
209
+ )
210
+ st.plotly_chart(fig)
211
+
212
+ def render_settings_tab(self):
213
+ """Render settings tab"""
214
+ st.header("Application Settings")
215
+
216
+ # Resume Upload
217
+ st.subheader("Resume")
218
+ resume_file = st.file_uploader("Upload your resume (TXT format)", type=['txt'])
219
+ if resume_file:
220
+ resume_text = resume_file.read().decode()
221
+ with open('templates/base_resume.txt', 'w') as f:
222
+ f.write(resume_text)
223
+ st.success("Resume uploaded successfully!")
224
+
225
+ # API Settings
226
+ st.subheader("API Settings")
227
+ api_key = st.text_input(
228
+ "OpenAI API Key",
229
+ value=os.getenv('OPENAI_API_KEY', ''),
230
+ type="password"
231
+ )
232
+ if st.button("Save API Key"):
233
+ with open('.env', 'w') as f:
234
+ f.write(f"OPENAI_API_KEY={api_key}")
235
+ st.success("API key saved!")
236
+
237
+ def run(self):
238
+ """Run the Streamlit application"""
239
+ st.title("AI Job Search Assistant")
240
+
241
+ # Render sidebar
242
+ self.render_sidebar()
243
+
244
+ # Main content tabs
245
+ tab1, tab2, tab3 = st.tabs(["Job Search", "Analytics", "Settings"])
246
+
247
+ with tab1:
248
+ self.render_job_search_tab()
249
+
250
+ with tab2:
251
+ self.render_analytics_tab()
252
+
253
+ with tab3:
254
+ self.render_settings_tab()
255
+
256
+ def main():
257
+ app = JobAssistantUI()
258
+ app.run()
259
+
260
+ if __name__ == "__main__":
261
+ main()