grasepard2 commited on
Commit
62a25eb
Β·
verified Β·
1 Parent(s): eefd3a8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +46 -47
app.py CHANGED
@@ -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": "#ef4444", "Positive": "#34d399"},
139
  title="Signal breakdown")
140
  fig.update_layout(**_styled_layout(height=420, showlegend=True))
141
  else:
@@ -150,22 +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_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,12 +174,12 @@ def _styled_layout(**kwargs) -> dict:
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,7 +198,7 @@ def build_flag_frequency_chart() -> go.Figure:
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,7 +212,7 @@ def build_risk_distribution_chart() -> go.Figure:
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,8 +228,8 @@ def build_score_distribution_chart() -> go.Figure:
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,13 +246,13 @@ def build_score_distribution_chart() -> go.Figure:
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
 
@@ -267,35 +267,36 @@ def render_kpi_cards() -> str:
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>")
@@ -388,8 +389,9 @@ def ai_chat(user_msg: str, history: list):
388
  reply += "\n\n" + reply_fb
389
  elif LLM_ENABLED:
390
  msgs = [{"role": "system", "content": DASHBOARD_SYSTEM}]
391
- for entry in (history or [])[-6:]:
392
- msgs.append(entry)
 
393
  msgs.append({"role": "user", "content": user_msg})
394
  try:
395
  r = llm_client.chat_completion(model=MODEL_NAME, messages=msgs,
@@ -412,10 +414,7 @@ def ai_chat(user_msg: str, history: list):
412
  }
413
  chart_out = chart_builders[directive["show"]]() if directive.get("show") in chart_builders else None
414
 
415
- new_history = (history or []) + [
416
- {"role": "user", "content": user_msg},
417
- {"role": "assistant", "content": reply},
418
- ]
419
  return new_history, "", chart_out
420
 
421
 
 
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": "#c53030", "Positive": "#2f855a"},
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_white",
154
+ paper_bgcolor="#fdfaf3",
155
+ plot_bgcolor="#fdfaf3",
156
+ font=dict(family="Geist, system-ui, -apple-system, sans-serif", color="#1a2238", 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(253,250,243,0.9)",
160
+ bordercolor="#d9cfb9", borderwidth=1,
161
+ font=dict(color="#4a5475", size=11)),
162
+ title=dict(font=dict(size=14, color="#1a2238", family="Geist, system-ui, sans-serif")),
163
+ xaxis=dict(gridcolor="#e6dcc7", zerolinecolor="#d9cfb9",
164
+ tickfont=dict(color="#4a5475", size=11),
165
+ title=dict(font=dict(color="#4a5475", size=12))),
166
+ yaxis=dict(gridcolor="#e6dcc7", zerolinecolor="#d9cfb9",
167
+ tickfont=dict(color="#4a5475", size=11),
168
+ title=dict(font=dict(color="#4a5475", 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_white",
178
+ paper_bgcolor="#fdfaf3",
179
+ plot_bgcolor="#fdfaf3",
180
+ font=dict(color="#1a2238", 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="#8a9099"))],
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, "#f4b8b1"], [1, "#e85a4f"]]),
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": "#2a9d8f", "Medium": "#e9a23b", "High": "#c53030"}
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="#e85a4f",
232
+ marker_line_color="#c53030", 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:#fdfaf3;padding:32px;text-align:center;'
250
+ 'border-radius:12px;border:1px solid #d9cfb9;">'
251
  '<div style="font-family:\'Geist Mono\',monospace;font-size:11px;'
252
+ 'color:#e85a4f;letter-spacing:0.08em;text-transform:uppercase;margin-bottom:12px;font-weight:600;">No Data</div>'
253
+ '<div style="color:#4a5475;font-size:14px;">'
254
+ 'Upload <code style="background:#f1ebe0;color:#7d4e8a;padding:2px 6px;border-radius:4px;'
255
+ 'font-family:\'Geist Mono\',monospace;font-size:0.85em;border:1px solid #e6dcc7;">'
256
  'job_description_data.xlsx</code> to populate metrics.'
257
  '</div></div>')
258
 
 
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="#e85a4f"):
271
  return f"""
272
+ <div style="background:#fdfaf3;border:1px solid #d9cfb9;border-radius:12px;
273
  padding:20px 22px;position:relative;overflow:hidden;
274
+ box-shadow:0 1px 0 rgba(255,255,255,0.7) inset, 0 2px 8px rgba(26, 34, 56, 0.04);
275
+ transition:border-color 0.15s, transform 0.15s;">
276
  <div style="font-family:'Geist Mono','SF Mono',monospace;
277
+ color:{accent_color};font-size:11px;font-weight:600;
278
  text-transform:uppercase;letter-spacing:0.08em;margin-bottom:14px;">
279
  {label}
280
  </div>
281
+ <div style="color:#1a2238;font-size:34px;font-weight:700;line-height:1;
282
  letter-spacing:-0.03em;margin-bottom:10px;
283
  font-family:'Geist',-apple-system,system-ui,sans-serif;">
284
  {value}
285
  </div>
286
  <div style="display:flex;align-items:center;gap:6px;
287
+ font-family:'Geist Mono',monospace;font-size:11px;color:#4a5475;">
288
  <span style="display:inline-block;width:6px;height:6px;border-radius:50%;
289
+ background:{accent_color};box-shadow:0 0 8px {accent_color}80;"></span>
290
  <span>{delta_text}</span>
291
  </div>
292
  </div>"""
293
 
294
  cards = [
295
+ card("Total.Jobs", f"{total_jobs}", "real labeled postings", "#e85a4f"),
296
+ card("Avg.Score", f"{avg_score:.1f}", "weighted across dataset", "#2a9d8f"),
297
+ card("High.Risk %", f"{high_pct:.0f}%", f"{risk_counts.get('High', 0)} postings flagged", "#c53030"),
298
  card("Top.Signal", top_flag.split(' ')[0].title() if top_flag != "β€”" else "β€”",
299
+ top_flag if top_flag != "β€”" else "no data", "#7d4e8a"),
300
  ]
301
  return ('<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));'
302
  'gap:12px;margin-bottom:32px;">' + "".join(cards) + "</div>")
 
389
  reply += "\n\n" + reply_fb
390
  elif LLM_ENABLED:
391
  msgs = [{"role": "system", "content": DASHBOARD_SYSTEM}]
392
+ for user_turn, bot_turn in (history or [])[-3:]:
393
+ msgs.append({"role": "user", "content": user_turn})
394
+ msgs.append({"role": "assistant", "content": bot_turn})
395
  msgs.append({"role": "user", "content": user_msg})
396
  try:
397
  r = llm_client.chat_completion(model=MODEL_NAME, messages=msgs,
 
414
  }
415
  chart_out = chart_builders[directive["show"]]() if directive.get("show") in chart_builders else None
416
 
417
+ new_history = (history or []) + [(user_msg, reply)]
 
 
 
418
  return new_history, "", chart_out
419
 
420