razaali10 commited on
Commit
23ee159
·
verified ·
1 Parent(s): b5f50bc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +114 -73
app.py CHANGED
@@ -24,9 +24,8 @@ def remove_duplicates(df: pd.DataFrame) -> pd.DataFrame:
24
 
25
 
26
  def compute_keyword_score(text: str, keywords: List[str]) -> int:
27
- """Simple keyword ranking: count of keyword hits (case-insensitive)."""
28
  text_l = (text or "").lower()
29
- return sum(text_l.count(k.lower()) for k in keywords if k.strip())
30
 
31
 
32
  # ======================================================
@@ -100,7 +99,7 @@ def extract_search_parameters(client, prompt: str) -> Dict[str, str]:
100
 
101
 
102
  # ======================================================
103
- # Job scraping (Per-board toggles)
104
  # ======================================================
105
 
106
  @st.cache_data(ttl=3600)
@@ -125,11 +124,6 @@ def get_indeed_jobs(
125
  return pd.DataFrame()
126
 
127
 
128
- def get_other_board_stub(board_name: str) -> pd.DataFrame:
129
- """Stub for future boards (toggle-safe)."""
130
- return pd.DataFrame()
131
-
132
-
133
  # ======================================================
134
  # Streamlit App
135
  # ======================================================
@@ -138,102 +132,142 @@ def main():
138
  st.set_page_config(page_title="Private Job Search", layout="centered")
139
  st.title("📄 Private Job Search, Rank & Download")
140
 
141
- # --- Inputs ---
 
 
142
  job_prompt = st.text_area(
143
  "Describe the job you are looking for",
144
- placeholder="e.g. Civil Engineer, Water Resources, Transportation in Alberta",
145
  height=120
146
  )
147
 
148
  api_key = st.text_input("Groq API Key", type="password")
149
 
150
- # --- Per-job-board toggles ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  st.subheader("Job Boards")
152
- colb1, colb2, colb3 = st.columns(3)
153
- with colb1:
154
- use_indeed = st.checkbox("Indeed", value=True)
155
- with colb2:
156
- use_glassdoor = st.checkbox("Glassdoor (coming soon)", value=False, disabled=True)
157
- with colb3:
158
- use_linkedin = st.checkbox("LinkedIn (coming soon)", value=False, disabled=True)
159
-
160
- # --- Filters ---
161
  st.subheader("Filters")
 
162
  posted_within_days = st.slider(
163
  "Posted within last (days)",
164
- min_value=1, max_value=30, value=7
 
 
165
  )
166
 
167
  radius_km = st.slider(
168
  "Search radius (km)",
169
- min_value=5, max_value=100, value=25, step=5
 
 
 
170
  )
171
 
172
- # --- Keyword ranking ---
 
 
173
  keywords_raw = st.text_input(
174
  "Keyword ranking (comma-separated)",
175
- placeholder="water, wastewater, stormwater, EPANET, WNTR"
176
  )
177
  keywords = [k.strip() for k in keywords_raw.split(",") if k.strip()]
178
 
179
- # --- Optional email ---
 
 
180
  send_email = st.checkbox("📧 Send results by email (optional)")
181
  email_address = st.text_input("Email address") if send_email else None
182
 
183
- # --- Action ---
184
- if st.button("🔍 Search Jobs", disabled=not job_prompt or not api_key):
 
 
 
 
 
185
  client = groq.Client(api_key=api_key)
186
 
187
  with st.spinner("Understanding your request..."):
188
  params = extract_search_parameters(client, job_prompt)
189
 
190
- all_jobs = []
191
-
192
- with st.spinner("Searching job boards..."):
193
- if use_indeed:
194
- indeed_df = get_indeed_jobs(
195
- params["search_term"],
196
- params["location"],
197
- radius_km,
198
- posted_within_days
199
- )
200
- if not indeed_df.empty:
201
- indeed_df["source"] = "Indeed"
202
- all_jobs.append(indeed_df)
203
-
204
- # Future boards (toggle-safe)
205
- if use_glassdoor:
206
- all_jobs.append(get_other_board_stub("Glassdoor"))
207
- if use_linkedin:
208
- all_jobs.append(get_other_board_stub("LinkedIn"))
209
-
210
- if not all_jobs:
211
- st.warning("No jobs found.")
212
  return
213
 
214
- jobs_df = pd.concat(all_jobs, ignore_index=True)
215
- jobs_df.fillna("", inplace=True)
216
- jobs_df = remove_duplicates(jobs_df)
217
-
218
- # --- Keyword ranking ---
219
- if keywords:
220
- jobs_df["keyword_score"] = jobs_df.apply(
221
- lambda r: compute_keyword_score(
222
- f"{r.get('title','')} {r.get('description','')}",
223
- keywords
224
- ),
225
- axis=1
226
  )
227
- else:
228
- jobs_df["keyword_score"] = 0
229
 
230
- # Sort by keyword score (desc) then date if available
231
- sort_cols = ["keyword_score"]
232
- jobs_df = jobs_df.sort_values(sort_cols, ascending=[False])
233
 
234
- st.success(f" Found {len(jobs_df)} jobs")
 
 
 
 
 
 
 
 
 
 
235
 
236
- # --- Download always available ---
 
 
 
 
 
 
 
 
 
237
  csv_data = jobs_df.to_csv(index=False).encode("utf-8")
238
  st.download_button(
239
  label="⬇️ Download Jobs (CSV)",
@@ -242,7 +276,9 @@ def main():
242
  mime="text/csv"
243
  )
244
 
245
- # --- Optional email ---
 
 
246
  if send_email:
247
  if not email_address:
248
  st.warning("Please enter an email address.")
@@ -256,15 +292,20 @@ def main():
256
  except Exception as e:
257
  st.error(f"Failed to send email: {e}")
258
 
259
- # --- Preview ---
260
- st.subheader("Preview (Top Results)")
 
 
261
  preview_cols = [
262
  c for c in [
263
- "source", "title", "company", "location",
264
  "keyword_score", "date_posted", "job_url"
265
  ] if c in jobs_df.columns
266
  ]
267
- st.dataframe(jobs_df[preview_cols].head(20), use_container_width=True)
 
 
 
268
 
269
 
270
  if __name__ == "__main__":
 
24
 
25
 
26
  def compute_keyword_score(text: str, keywords: List[str]) -> int:
 
27
  text_l = (text or "").lower()
28
+ return sum(text_l.count(k.lower()) for k in keywords if k)
29
 
30
 
31
  # ======================================================
 
99
 
100
 
101
  # ======================================================
102
+ # Job scraping
103
  # ======================================================
104
 
105
  @st.cache_data(ttl=3600)
 
124
  return pd.DataFrame()
125
 
126
 
 
 
 
 
 
127
  # ======================================================
128
  # Streamlit App
129
  # ======================================================
 
132
  st.set_page_config(page_title="Private Job Search", layout="centered")
133
  st.title("📄 Private Job Search, Rank & Download")
134
 
135
+ # --------------------------------------------------
136
+ # Job description
137
+ # --------------------------------------------------
138
  job_prompt = st.text_area(
139
  "Describe the job you are looking for",
140
+ placeholder="e.g. Civil Engineer, Water Resources, Transportation",
141
  height=120
142
  )
143
 
144
  api_key = st.text_input("Groq API Key", type="password")
145
 
146
+ # --------------------------------------------------
147
+ # City selection
148
+ # --------------------------------------------------
149
+ st.subheader("Location")
150
+
151
+ predefined_cities = [
152
+ "Use AI / Prompt Location",
153
+ "Calgary, AB",
154
+ "Edmonton, AB",
155
+ "Toronto, ON",
156
+ "Vancouver, BC",
157
+ "Mississauga, ON",
158
+ "Brampton, ON",
159
+ "Ottawa, ON",
160
+ "Hamilton, ON",
161
+ "Custom city..."
162
+ ]
163
+
164
+ selected_city = st.selectbox("Select city", predefined_cities)
165
+
166
+ custom_city = ""
167
+ if selected_city == "Custom city...":
168
+ custom_city = st.text_input(
169
+ "Enter city (e.g., Red Deer, AB or Surrey, BC)"
170
+ )
171
+
172
+ # --------------------------------------------------
173
+ # Job boards
174
+ # --------------------------------------------------
175
  st.subheader("Job Boards")
176
+ use_indeed = st.checkbox("Indeed", value=True)
177
+
178
+ # --------------------------------------------------
179
+ # Filters
180
+ # --------------------------------------------------
 
 
 
 
181
  st.subheader("Filters")
182
+
183
  posted_within_days = st.slider(
184
  "Posted within last (days)",
185
+ min_value=1,
186
+ max_value=30,
187
+ value=7
188
  )
189
 
190
  radius_km = st.slider(
191
  "Search radius (km)",
192
+ min_value=5,
193
+ max_value=100,
194
+ value=25,
195
+ step=5
196
  )
197
 
198
+ # --------------------------------------------------
199
+ # Keyword ranking
200
+ # --------------------------------------------------
201
  keywords_raw = st.text_input(
202
  "Keyword ranking (comma-separated)",
203
+ placeholder="water, wastewater, stormwater, EPANET"
204
  )
205
  keywords = [k.strip() for k in keywords_raw.split(",") if k.strip()]
206
 
207
+ # --------------------------------------------------
208
+ # Optional email
209
+ # --------------------------------------------------
210
  send_email = st.checkbox("📧 Send results by email (optional)")
211
  email_address = st.text_input("Email address") if send_email else None
212
 
213
+ # --------------------------------------------------
214
+ # Action
215
+ # --------------------------------------------------
216
+ if st.button(
217
+ "🔍 Search Jobs",
218
+ disabled=not job_prompt or not api_key
219
+ ):
220
  client = groq.Client(api_key=api_key)
221
 
222
  with st.spinner("Understanding your request..."):
223
  params = extract_search_parameters(client, job_prompt)
224
 
225
+ # Resolve final location
226
+ if selected_city == "Use AI / Prompt Location":
227
+ location = params.get("location", "Canada")
228
+ elif selected_city == "Custom city...":
229
+ location = custom_city if custom_city else params.get("location", "Canada")
230
+ else:
231
+ location = selected_city
232
+
233
+ if not use_indeed:
234
+ st.warning("No job boards selected.")
 
 
 
 
 
 
 
 
 
 
 
 
235
  return
236
 
237
+ with st.spinner("Searching jobs..."):
238
+ jobs_df = get_indeed_jobs(
239
+ params["search_term"],
240
+ location,
241
+ radius_km,
242
+ posted_within_days
 
 
 
 
 
 
243
  )
 
 
244
 
245
+ if jobs_df.empty:
246
+ st.warning("No jobs found.")
247
+ return
248
 
249
+ jobs_df.fillna("", inplace=True)
250
+ jobs_df = remove_duplicates(jobs_df)
251
+
252
+ # Keyword ranking
253
+ jobs_df["keyword_score"] = jobs_df.apply(
254
+ lambda r: compute_keyword_score(
255
+ f"{r.get('title','')} {r.get('description','')}",
256
+ keywords
257
+ ),
258
+ axis=1
259
+ )
260
 
261
+ jobs_df = jobs_df.sort_values(
262
+ by="keyword_score",
263
+ ascending=False
264
+ )
265
+
266
+ st.success(f"✅ Found {len(jobs_df)} jobs for **{location}**")
267
+
268
+ # --------------------------------------------------
269
+ # Download
270
+ # --------------------------------------------------
271
  csv_data = jobs_df.to_csv(index=False).encode("utf-8")
272
  st.download_button(
273
  label="⬇️ Download Jobs (CSV)",
 
276
  mime="text/csv"
277
  )
278
 
279
+ # --------------------------------------------------
280
+ # Optional email
281
+ # --------------------------------------------------
282
  if send_email:
283
  if not email_address:
284
  st.warning("Please enter an email address.")
 
292
  except Exception as e:
293
  st.error(f"Failed to send email: {e}")
294
 
295
+ # --------------------------------------------------
296
+ # Preview
297
+ # --------------------------------------------------
298
+ st.subheader("Preview (Top 20 Results)")
299
  preview_cols = [
300
  c for c in [
301
+ "title", "company", "location",
302
  "keyword_score", "date_posted", "job_url"
303
  ] if c in jobs_df.columns
304
  ]
305
+ st.dataframe(
306
+ jobs_df[preview_cols].head(20),
307
+ use_container_width=True
308
+ )
309
 
310
 
311
  if __name__ == "__main__":