grasepard2 commited on
Commit
752a7c4
Β·
verified Β·
1 Parent(s): 400cb90

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -45
app.py CHANGED
@@ -58,8 +58,8 @@ RED_FLAGS = [
58
  ("benefits clearly mentioned", -4, ["health insurance", "paid leave", "meal vouchers", "transport", "benefits include", "profit-sharing"]),
59
  ]
60
 
61
- CHART_PALETTE = ["#4f46e5", "#06b6d4", "#0ea5e9", "#8b5cf6", "#ec4899",
62
- "#f59e0b", "#10b981", "#ef4444", "#6366f1", "#14b8a6"]
63
 
64
  # =========================================================
65
  # DATA LOADING
@@ -135,7 +135,7 @@ def analyze_job(text: str) -> Tuple[str, int, str, go.Figure]:
135
  cdf = pd.DataFrame(detected, columns=["Signal", "Weight"])
136
  cdf["Type"] = cdf["Weight"].apply(lambda w: "Red flag" if w > 0 else "Positive")
137
  fig = px.bar(cdf, x="Weight", y="Signal", color="Type", orientation="h",
138
- color_discrete_map={"Red flag": "#dc2626", "Positive": "#10b981"},
139
  title="Signal breakdown")
140
  fig.update_layout(**_styled_layout(height=420, showlegend=True))
141
  else:
@@ -150,15 +150,22 @@ def analyze_job(text: str) -> Tuple[str, int, str, go.Figure]:
150
 
151
  def _styled_layout(**kwargs) -> dict:
152
  defaults = dict(
153
- template="plotly_white",
154
- paper_bgcolor="#ffffff",
155
- plot_bgcolor="#ffffff",
156
- font=dict(family="Inter, system-ui, -apple-system, sans-serif", color="#0f172a", size=12),
157
  margin=dict(l=60, r=20, t=70, b=70),
158
  legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1,
159
- bgcolor="rgba(255,255,255,0.9)",
160
- bordercolor="#e2e8f0", borderwidth=1),
161
- title=dict(font=dict(size=15, color="#0f172a", family="Inter, system-ui, sans-serif")),
 
 
 
 
 
 
 
162
  )
163
  defaults.update(kwargs)
164
  return defaults
@@ -167,10 +174,12 @@ def _styled_layout(**kwargs) -> dict:
167
  def _empty_chart(title: str) -> go.Figure:
168
  fig = go.Figure()
169
  fig.update_layout(
170
- title=title, height=420, template="plotly_white",
171
- paper_bgcolor="#ffffff",
172
- annotations=[dict(text="(no data)", x=0.5, y=0.5, xref="paper", yref="paper",
173
- showarrow=False, font=dict(size=14, color="#94a3b8"))],
 
 
174
  )
175
  return fig
176
 
@@ -189,7 +198,7 @@ def build_flag_frequency_chart() -> go.Figure:
189
  fig = go.Figure(go.Bar(
190
  y=counts.index[::-1], x=counts.values[::-1], orientation="h",
191
  marker=dict(color=counts.values[::-1],
192
- colorscale=[[0, "#c7d2fe"], [1, "#4f46e5"]]),
193
  hovertemplate="<b>%{y}</b><br>Detected in %{x} jobs<extra></extra>",
194
  ))
195
  fig.update_layout(**_styled_layout(
@@ -203,7 +212,7 @@ def build_risk_distribution_chart() -> go.Figure:
203
  if DF.empty or "Risk Level" not in DF.columns:
204
  return _empty_chart("Dataset not loaded")
205
  counts = DF["Risk Level"].value_counts()
206
- colors = {"Low": "#10b981", "Medium": "#f59e0b", "High": "#dc2626"}
207
  fig = go.Figure(go.Pie(
208
  labels=counts.index, values=counts.values,
209
  marker=dict(colors=[colors.get(l, "#888") for l in counts.index]),
@@ -219,7 +228,8 @@ def build_score_distribution_chart() -> go.Figure:
219
  return _empty_chart("Dataset not loaded")
220
  scores = DF["Score"].dropna()
221
  fig = go.Figure(go.Histogram(
222
- x=scores, nbinsx=15, marker_color="#4f46e5",
 
223
  hovertemplate="Score range: %{x}<br>Jobs: %{y}<extra></extra>",
224
  ))
225
  fig.update_layout(**_styled_layout(
@@ -236,11 +246,14 @@ def build_score_distribution_chart() -> go.Figure:
236
 
237
  def render_kpi_cards() -> str:
238
  if DF.empty:
239
- return ('<div style="background:#fff;padding:32px;text-align:center;'
240
- 'border-radius:20px;border:1px solid #e2e8f0;">'
241
- '<div style="font-size:32px;margin-bottom:8px;">πŸ“Š</div>'
242
- '<div style="color:#475569;font-size:14px;">'
243
- 'Upload <code style="background:#eef2ff;color:#4f46e5;padding:2px 6px;border-radius:4px;">job_description_data.xlsx</code> to populate KPIs.'
 
 
 
244
  '</div></div>')
245
 
246
  total_jobs = len(DF)
@@ -254,37 +267,38 @@ def render_kpi_cards() -> str:
254
  all_flags.extend(label for label, _ in extract_flag_labels(str(cell)))
255
  top_flag = pd.Series(all_flags).value_counts().index[0] if all_flags else "β€”"
256
 
257
- def card(label, value, sublabel, accent):
258
  return f"""
259
- <div style="position:relative;background:#ffffff;border-radius:16px;
260
- padding:24px 22px;border:1px solid #e2e8f0;
261
- box-shadow:0 1px 2px rgba(15,23,42,0.04),0 1px 3px rgba(15,23,42,0.06);
262
- overflow:hidden;transition:transform 0.15s,box-shadow 0.15s;">
263
- <div style="position:absolute;top:0;left:0;right:0;height:3px;
264
- background:linear-gradient(90deg,{accent},#06b6d4);"></div>
265
- <div style="color:#94a3b8;font-size:11px;font-weight:600;
266
- text-transform:uppercase;letter-spacing:0.08em;margin-bottom:12px;">
267
  {label}
268
  </div>
269
- <div style="color:#0f172a;font-size:32px;font-weight:800;line-height:1;
270
- letter-spacing:-0.03em;margin-bottom:8px;
271
- font-family:'Inter',-apple-system,system-ui,sans-serif;">
272
  {value}
273
  </div>
274
- <div style="color:#475569;font-size:13px;font-weight:500;">
275
- {sublabel}
 
 
 
276
  </div>
277
  </div>"""
278
 
279
  cards = [
280
- card("Jobs Analyzed", f"{total_jobs}", "Real labeled postings", "#4f46e5"),
281
- card("Avg Risk Score", f"{avg_score:.1f}", "Across all postings", "#0ea5e9"),
282
- card("High-Risk Share", f"{high_pct:.0f}%", "Postings flagged High", "#dc2626"),
283
- card("Top Red Flag", top_flag.split(' ')[0].title() if top_flag != "β€”" else "β€”",
284
- top_flag if top_flag != "β€”" else "No data", "#ea580c"),
285
  ]
286
- return ('<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));'
287
- 'gap:16px;margin-bottom:32px;">' + "".join(cards) + "</div>")
288
 
289
 
290
  # =========================================================
@@ -415,8 +429,9 @@ def load_css() -> str:
415
  with gr.Blocks(title="Job Risk Analyzer β€” CS1 Group 14") as demo:
416
 
417
  gr.Markdown(
418
- "# Job Description Risk Analyzer\n"
419
- "*AI-powered red-flag detection β€” CS1 Group 14 β€” AI for Big Data Management*",
 
420
  elem_id="escp_title",
421
  )
422
 
 
58
  ("benefits clearly mentioned", -4, ["health insurance", "paid leave", "meal vouchers", "transport", "benefits include", "profit-sharing"]),
59
  ]
60
 
61
+ CHART_PALETTE = ["#34d399", "#60a5fa", "#f472b6", "#fbbf24", "#a78bfa",
62
+ "#22d3ee", "#fb7185", "#84cc16", "#f97316", "#e879f9"]
63
 
64
  # =========================================================
65
  # DATA LOADING
 
135
  cdf = pd.DataFrame(detected, columns=["Signal", "Weight"])
136
  cdf["Type"] = cdf["Weight"].apply(lambda w: "Red flag" if w > 0 else "Positive")
137
  fig = px.bar(cdf, x="Weight", y="Signal", color="Type", orientation="h",
138
+ color_discrete_map={"Red flag": "#ef4444", "Positive": "#34d399"},
139
  title="Signal breakdown")
140
  fig.update_layout(**_styled_layout(height=420, showlegend=True))
141
  else:
 
150
 
151
  def _styled_layout(**kwargs) -> dict:
152
  defaults = dict(
153
+ template="plotly_dark",
154
+ paper_bgcolor="#131313",
155
+ plot_bgcolor="#131313",
156
+ font=dict(family="Geist, system-ui, -apple-system, sans-serif", color="#ededed", size=12),
157
  margin=dict(l=60, r=20, t=70, b=70),
158
  legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1,
159
+ bgcolor="rgba(19,19,19,0.9)",
160
+ bordercolor="#262626", borderwidth=1,
161
+ font=dict(color="#a1a1a1", size=11)),
162
+ title=dict(font=dict(size=14, color="#ededed", family="Geist, system-ui, sans-serif")),
163
+ xaxis=dict(gridcolor="#1f1f1f", zerolinecolor="#262626",
164
+ tickfont=dict(color="#a1a1a1", size=11),
165
+ title=dict(font=dict(color="#a1a1a1", size=12))),
166
+ yaxis=dict(gridcolor="#1f1f1f", zerolinecolor="#262626",
167
+ tickfont=dict(color="#a1a1a1", size=11),
168
+ title=dict(font=dict(color="#a1a1a1", size=12))),
169
  )
170
  defaults.update(kwargs)
171
  return defaults
 
174
  def _empty_chart(title: str) -> go.Figure:
175
  fig = go.Figure()
176
  fig.update_layout(
177
+ title=title, height=420, template="plotly_dark",
178
+ paper_bgcolor="#131313",
179
+ plot_bgcolor="#131313",
180
+ font=dict(color="#ededed", family="Geist, system-ui, sans-serif"),
181
+ annotations=[dict(text="(no data available)", x=0.5, y=0.5, xref="paper", yref="paper",
182
+ showarrow=False, font=dict(size=13, color="#666"))],
183
  )
184
  return fig
185
 
 
198
  fig = go.Figure(go.Bar(
199
  y=counts.index[::-1], x=counts.values[::-1], orientation="h",
200
  marker=dict(color=counts.values[::-1],
201
+ colorscale=[[0, "#0d4d3a"], [1, "#34d399"]]),
202
  hovertemplate="<b>%{y}</b><br>Detected in %{x} jobs<extra></extra>",
203
  ))
204
  fig.update_layout(**_styled_layout(
 
212
  if DF.empty or "Risk Level" not in DF.columns:
213
  return _empty_chart("Dataset not loaded")
214
  counts = DF["Risk Level"].value_counts()
215
+ colors = {"Low": "#34d399", "Medium": "#f59e0b", "High": "#ef4444"}
216
  fig = go.Figure(go.Pie(
217
  labels=counts.index, values=counts.values,
218
  marker=dict(colors=[colors.get(l, "#888") for l in counts.index]),
 
228
  return _empty_chart("Dataset not loaded")
229
  scores = DF["Score"].dropna()
230
  fig = go.Figure(go.Histogram(
231
+ x=scores, nbinsx=15, marker_color="#34d399",
232
+ marker_line_color="#0d4d3a", marker_line_width=1,
233
  hovertemplate="Score range: %{x}<br>Jobs: %{y}<extra></extra>",
234
  ))
235
  fig.update_layout(**_styled_layout(
 
246
 
247
  def render_kpi_cards() -> str:
248
  if DF.empty:
249
+ return ('<div style="background:#131313;padding:32px;text-align:center;'
250
+ 'border-radius:12px;border:1px solid #262626;">'
251
+ '<div style="font-family:\'Geist Mono\',monospace;font-size:11px;'
252
+ 'color:#666;letter-spacing:0.08em;text-transform:uppercase;margin-bottom:12px;">No Data</div>'
253
+ '<div style="color:#a1a1a1;font-size:14px;">'
254
+ 'Upload <code style="background:#161616;color:#34d399;padding:2px 6px;border-radius:4px;'
255
+ 'font-family:\'Geist Mono\',monospace;font-size:0.85em;border:1px solid #1f1f1f;">'
256
+ 'job_description_data.xlsx</code> to populate metrics.'
257
  '</div></div>')
258
 
259
  total_jobs = len(DF)
 
267
  all_flags.extend(label for label, _ in extract_flag_labels(str(cell)))
268
  top_flag = pd.Series(all_flags).value_counts().index[0] if all_flags else "β€”"
269
 
270
+ def card(label, value, delta_text, accent_color="#34d399"):
271
  return f"""
272
+ <div style="background:#131313;border:1px solid #262626;border-radius:12px;
273
+ padding:20px 22px;position:relative;overflow:hidden;
274
+ transition:border-color 0.15s, background 0.15s;">
275
+ <div style="font-family:'Geist Mono','SF Mono',monospace;
276
+ color:#666;font-size:11px;font-weight:500;
277
+ text-transform:uppercase;letter-spacing:0.08em;margin-bottom:14px;">
 
 
278
  {label}
279
  </div>
280
+ <div style="color:#ededed;font-size:32px;font-weight:600;line-height:1;
281
+ letter-spacing:-0.03em;margin-bottom:10px;
282
+ font-family:'Geist',-apple-system,system-ui,sans-serif;">
283
  {value}
284
  </div>
285
+ <div style="display:flex;align-items:center;gap:6px;
286
+ font-family:'Geist Mono',monospace;font-size:11px;color:#a1a1a1;">
287
+ <span style="display:inline-block;width:6px;height:6px;border-radius:50%;
288
+ background:{accent_color};box-shadow:0 0 8px {accent_color};"></span>
289
+ <span>{delta_text}</span>
290
  </div>
291
  </div>"""
292
 
293
  cards = [
294
+ card("Total.Jobs", f"{total_jobs}", "real labeled postings", "#34d399"),
295
+ card("Avg.Score", f"{avg_score:.1f}", "weighted across dataset", "#60a5fa"),
296
+ card("High.Risk %", f"{high_pct:.0f}%", f"{risk_counts.get('High', 0)} postings flagged", "#ef4444"),
297
+ card("Top.Signal", top_flag.split(' ')[0].title() if top_flag != "β€”" else "β€”",
298
+ top_flag if top_flag != "β€”" else "no data", "#f59e0b"),
299
  ]
300
+ return ('<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));'
301
+ 'gap:12px;margin-bottom:32px;">' + "".join(cards) + "</div>")
302
 
303
 
304
  # =========================================================
 
429
  with gr.Blocks(title="Job Risk Analyzer β€” CS1 Group 14") as demo:
430
 
431
  gr.Markdown(
432
+ "# Job Risk Analyzer\n"
433
+ "Detect hidden risk patterns in job postings using a weighted signal taxonomy "
434
+ "calibrated on 47 real labeled descriptions.",
435
  elem_id="escp_title",
436
  )
437