XRachel commited on
Commit
eb0b8e1
·
verified ·
1 Parent(s): e96a2d9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +26 -74
app.py CHANGED
@@ -1,5 +1,3 @@
1
- print("=== RUNNING workshop/app.py ===")
2
-
3
  import os
4
  import re
5
  import json
@@ -109,7 +107,6 @@ def run_notebook(nb_name: str) -> str:
109
  )
110
  return f"Executed {nb_name}"
111
 
112
-
113
  def run_datacreation() -> str:
114
  try:
115
  log = run_notebook(NB1)
@@ -118,7 +115,6 @@ def run_datacreation() -> str:
118
  except Exception as e:
119
  return f"FAILED {e}\n\n{traceback.format_exc()[-2000:]}"
120
 
121
-
122
  def run_pythonanalysis() -> str:
123
  try:
124
  log = run_notebook(NB2)
@@ -133,7 +129,6 @@ def run_pythonanalysis() -> str:
133
  except Exception as e:
134
  return f"FAILED {e}\n\n{traceback.format_exc()[-2000:]}"
135
 
136
-
137
  def run_r() -> str:
138
  try:
139
  log = run_notebook(NB3)
@@ -148,7 +143,6 @@ def run_r() -> str:
148
  except Exception as e:
149
  return f"FAILED {e}\n\n{traceback.format_exc()[-2000:]}"
150
 
151
-
152
  def run_full_pipeline() -> str:
153
  logs = []
154
  logs.append("=" * 50)
@@ -167,7 +161,6 @@ def run_full_pipeline() -> str:
167
  logs.append(run_r())
168
  return "\n".join(logs)
169
 
170
-
171
  # =========================================================
172
  # GALLERY LOADERS
173
  # =========================================================
@@ -181,7 +174,6 @@ def _load_all_figures() -> List[Tuple[str, str]]:
181
  items.append((str(p), f"R | {p.stem.replace('_', ' ').title()}"))
182
  return items
183
 
184
-
185
  def _load_table_safe(path: Path) -> pd.DataFrame:
186
  try:
187
  if path.suffix == ".json":
@@ -193,24 +185,21 @@ def _load_table_safe(path: Path) -> pd.DataFrame:
193
  except Exception as e:
194
  return pd.DataFrame([{"error": str(e)}])
195
 
196
-
197
  def refresh_gallery():
198
  """Called when user clicks Refresh on Gallery tab."""
199
  figures = _load_all_figures()
200
  idx = artifacts_index()
201
 
202
- # Build table choices
203
  table_choices = []
204
  for scope in ("python", "r"):
205
  for name in idx[scope]["tables"]:
206
  table_choices.append(f"{scope}/{name}")
207
 
208
- # Default: show first table if available
209
  default_df = pd.DataFrame()
210
  if table_choices:
211
- parts = table_choices[0].split("/", 1)
212
- base = PY_TAB_DIR if parts[0] == "python" else R_TAB_DIR
213
- default_df = _load_table_safe(base / parts[1])
214
 
215
  return (
216
  figures if figures else [],
@@ -218,7 +207,6 @@ def refresh_gallery():
218
  default_df,
219
  )
220
 
221
-
222
  def on_table_select(choice: str):
223
  if not choice or "/" not in choice:
224
  return pd.DataFrame([{"hint": "Select a table above."}])
@@ -231,7 +219,6 @@ def on_table_select(choice: str):
231
  return pd.DataFrame([{"error": f"File not found: {path}"}])
232
  return _load_table_safe(path)
233
 
234
-
235
  # =========================================================
236
  # KPI LOADER
237
  # =========================================================
@@ -245,7 +232,6 @@ def load_kpis() -> Dict[str, Any]:
245
  pass
246
  return {}
247
 
248
-
249
  # =========================================================
250
  # AI DASHBOARD (Tab 3) -- LLM picks what to display
251
  # =========================================================
@@ -282,7 +268,6 @@ RULES:
282
  JSON_BLOCK_RE = re.compile(r"```json\s*(\{.*?\})\s*```", re.DOTALL)
283
  FALLBACK_JSON_RE = re.compile(r"\{[^{}]*\"show\"[^{}]*\}", re.DOTALL)
284
 
285
-
286
  def _parse_display_directive(text: str) -> Dict[str, str]:
287
  m = JSON_BLOCK_RE.search(text)
288
  if m:
@@ -298,12 +283,10 @@ def _parse_display_directive(text: str) -> Dict[str, str]:
298
  pass
299
  return {"show": "none"}
300
 
301
-
302
  def _clean_response(text: str) -> str:
303
  """Strip the JSON directive block from the displayed response."""
304
  return JSON_BLOCK_RE.sub("", text).strip()
305
 
306
-
307
  def ai_chat(user_msg: str, history: list):
308
  """Chat function for the AI Dashboard tab."""
309
  if not user_msg or not user_msg.strip():
@@ -344,7 +327,6 @@ def ai_chat(user_msg: str, history: list):
344
  reply_fb, directive = _keyword_fallback(user_msg, idx, kpis)
345
  reply += "\n\n" + reply_fb
346
 
347
- # Resolve artifact paths
348
  fig_out = None
349
  tab_out = None
350
  show = directive.get("show", "none")
@@ -372,7 +354,6 @@ def ai_chat(user_msg: str, history: list):
372
 
373
  return new_history, "", fig_out, tab_out
374
 
375
-
376
  def _keyword_fallback(msg: str, idx: Dict, kpis: Dict) -> Tuple[str, Dict]:
377
  """Simple keyword matcher when LLM is unavailable."""
378
  msg_lower = msg.lower()
@@ -449,7 +430,6 @@ def _keyword_fallback(msg: str, idx: Dict, kpis: Dict) -> Tuple[str, Dict]:
449
  {"show": "table", "scope": "python", "filename": "df_dashboard.csv"},
450
  )
451
 
452
- # Default
453
  return (
454
  f"I can show you various analyses. {kpi_text}\n\n"
455
  "Try asking about: **sales trends**, **sentiment**, **ARIMA forecasts**, "
@@ -457,28 +437,27 @@ def _keyword_fallback(msg: str, idx: Dict, kpis: Dict) -> Tuple[str, Dict]:
457
  {"show": "none"},
458
  )
459
 
460
-
461
  # =========================================================
462
- # UI
463
  # =========================================================
464
 
465
- ensure_dirs()
466
-
467
-
468
  def load_css() -> str:
469
  css_path = BASE_DIR / "style.css"
470
- print(">>> CSS PATH:", css_path, "exists=", css_path.exists())
471
  if css_path.exists():
472
- txt = css_path.read_text(encoding="utf-8")
473
- print(">>> CSS LEN:", len(txt))
474
- return txt
475
- print(">>> CSS NOT FOUND")
476
  return ""
477
 
 
 
 
478
 
479
- #with gr.Blocks(title="RX12 Workshop App") as demo:
480
 
481
- with gr.Blocks(title="RX12 Workshop App", css=load_css()) as demo:
 
 
 
 
482
 
483
  gr.Markdown(
484
  "# RX12 - Intro to Python and R - Workshop App\n"
@@ -490,37 +469,21 @@ with gr.Blocks(title="RX12 Workshop App", css=load_css()) as demo:
490
  # TAB 1 -- Pipeline Runner
491
  # ===========================================================
492
  with gr.Tab("Pipeline Runner"):
493
- gr.Markdown(
494
- )
495
 
496
  with gr.Row():
497
  with gr.Column(scale=1):
498
- btn_nb1 = gr.Button(
499
- "Step 1: Data Creation",
500
- variant="secondary",
501
- )
502
- gr.Markdown(
503
- )
504
  with gr.Column(scale=1):
505
- btn_nb2 = gr.Button(
506
- "Step 2a: Python Analysis",
507
- variant="secondary",
508
- )
509
- gr.Markdown(
510
- )
511
  with gr.Column(scale=1):
512
- btn_r = gr.Button(
513
- "Step 2b: R Analysis",
514
- variant="secondary",
515
- )
516
- gr.Markdown(
517
- )
518
 
519
  with gr.Row():
520
- btn_all = gr.Button(
521
- "Run All 3 Steps",
522
- variant="primary",
523
- )
524
 
525
  run_log = gr.Textbox(
526
  label="Execution Log",
@@ -585,17 +548,14 @@ with gr.Blocks(title="RX12 Workshop App", css=load_css()) as demo:
585
  + (
586
  "*LLM is active.*"
587
  if LLM_ENABLED
588
- else "*No API key detected \u2014 using keyword matching. "
589
  "Set `HF_API_KEY` in Space secrets for full LLM support.*"
590
  )
591
  )
592
 
593
  with gr.Row(equal_height=True):
594
  with gr.Column(scale=1):
595
- chatbot = gr.Chatbot(
596
- label="Conversation",
597
- height=380,
598
- )
599
  user_input = gr.Textbox(
600
  label="Ask about your data",
601
  placeholder="e.g. Show me sales trends / What drives revenue? / Compare forecast models",
@@ -614,14 +574,8 @@ with gr.Blocks(title="RX12 Workshop App", css=load_css()) as demo:
614
  )
615
 
616
  with gr.Column(scale=1):
617
- ai_figure = gr.Image(
618
- label="Visualisation",
619
- height=350,
620
- )
621
- ai_table = gr.Dataframe(
622
- label="Data Table",
623
- interactive=False,
624
- )
625
 
626
  user_input.submit(
627
  ai_chat,
@@ -630,5 +584,3 @@ with gr.Blocks(title="RX12 Workshop App", css=load_css()) as demo:
630
  )
631
 
632
  demo.launch(allowed_paths=[str(BASE_DIR)])
633
-
634
- #demo.launch(css=load_css(), allowed_paths=[str(BASE_DIR)])
 
 
 
1
  import os
2
  import re
3
  import json
 
107
  )
108
  return f"Executed {nb_name}"
109
 
 
110
  def run_datacreation() -> str:
111
  try:
112
  log = run_notebook(NB1)
 
115
  except Exception as e:
116
  return f"FAILED {e}\n\n{traceback.format_exc()[-2000:]}"
117
 
 
118
  def run_pythonanalysis() -> str:
119
  try:
120
  log = run_notebook(NB2)
 
129
  except Exception as e:
130
  return f"FAILED {e}\n\n{traceback.format_exc()[-2000:]}"
131
 
 
132
  def run_r() -> str:
133
  try:
134
  log = run_notebook(NB3)
 
143
  except Exception as e:
144
  return f"FAILED {e}\n\n{traceback.format_exc()[-2000:]}"
145
 
 
146
  def run_full_pipeline() -> str:
147
  logs = []
148
  logs.append("=" * 50)
 
161
  logs.append(run_r())
162
  return "\n".join(logs)
163
 
 
164
  # =========================================================
165
  # GALLERY LOADERS
166
  # =========================================================
 
174
  items.append((str(p), f"R | {p.stem.replace('_', ' ').title()}"))
175
  return items
176
 
 
177
  def _load_table_safe(path: Path) -> pd.DataFrame:
178
  try:
179
  if path.suffix == ".json":
 
185
  except Exception as e:
186
  return pd.DataFrame([{"error": str(e)}])
187
 
 
188
  def refresh_gallery():
189
  """Called when user clicks Refresh on Gallery tab."""
190
  figures = _load_all_figures()
191
  idx = artifacts_index()
192
 
 
193
  table_choices = []
194
  for scope in ("python", "r"):
195
  for name in idx[scope]["tables"]:
196
  table_choices.append(f"{scope}/{name}")
197
 
 
198
  default_df = pd.DataFrame()
199
  if table_choices:
200
+ scope, name = table_choices[0].split("/", 1)
201
+ base = PY_TAB_DIR if scope == "python" else R_TAB_DIR
202
+ default_df = _load_table_safe(base / name)
203
 
204
  return (
205
  figures if figures else [],
 
207
  default_df,
208
  )
209
 
 
210
  def on_table_select(choice: str):
211
  if not choice or "/" not in choice:
212
  return pd.DataFrame([{"hint": "Select a table above."}])
 
219
  return pd.DataFrame([{"error": f"File not found: {path}"}])
220
  return _load_table_safe(path)
221
 
 
222
  # =========================================================
223
  # KPI LOADER
224
  # =========================================================
 
232
  pass
233
  return {}
234
 
 
235
  # =========================================================
236
  # AI DASHBOARD (Tab 3) -- LLM picks what to display
237
  # =========================================================
 
268
  JSON_BLOCK_RE = re.compile(r"```json\s*(\{.*?\})\s*```", re.DOTALL)
269
  FALLBACK_JSON_RE = re.compile(r"\{[^{}]*\"show\"[^{}]*\}", re.DOTALL)
270
 
 
271
  def _parse_display_directive(text: str) -> Dict[str, str]:
272
  m = JSON_BLOCK_RE.search(text)
273
  if m:
 
283
  pass
284
  return {"show": "none"}
285
 
 
286
  def _clean_response(text: str) -> str:
287
  """Strip the JSON directive block from the displayed response."""
288
  return JSON_BLOCK_RE.sub("", text).strip()
289
 
 
290
  def ai_chat(user_msg: str, history: list):
291
  """Chat function for the AI Dashboard tab."""
292
  if not user_msg or not user_msg.strip():
 
327
  reply_fb, directive = _keyword_fallback(user_msg, idx, kpis)
328
  reply += "\n\n" + reply_fb
329
 
 
330
  fig_out = None
331
  tab_out = None
332
  show = directive.get("show", "none")
 
354
 
355
  return new_history, "", fig_out, tab_out
356
 
 
357
  def _keyword_fallback(msg: str, idx: Dict, kpis: Dict) -> Tuple[str, Dict]:
358
  """Simple keyword matcher when LLM is unavailable."""
359
  msg_lower = msg.lower()
 
430
  {"show": "table", "scope": "python", "filename": "df_dashboard.csv"},
431
  )
432
 
 
433
  return (
434
  f"I can show you various analyses. {kpi_text}\n\n"
435
  "Try asking about: **sales trends**, **sentiment**, **ARIMA forecasts**, "
 
437
  {"show": "none"},
438
  )
439
 
 
440
  # =========================================================
441
+ # CSS LOADER (robust injection via <style> tag)
442
  # =========================================================
443
 
 
 
 
444
  def load_css() -> str:
445
  css_path = BASE_DIR / "style.css"
 
446
  if css_path.exists():
447
+ return css_path.read_text(encoding="utf-8")
 
 
 
448
  return ""
449
 
450
+ # =========================================================
451
+ # UI
452
+ # =========================================================
453
 
454
+ ensure_dirs()
455
 
456
+ with gr.Blocks(title="RX12 Workshop App") as demo:
457
+ # ✅ Robust CSS injection (works across gradio versions)
458
+ css_text = load_css()
459
+ if css_text.strip():
460
+ gr.HTML(f"<style>{css_text}</style>")
461
 
462
  gr.Markdown(
463
  "# RX12 - Intro to Python and R - Workshop App\n"
 
469
  # TAB 1 -- Pipeline Runner
470
  # ===========================================================
471
  with gr.Tab("Pipeline Runner"):
472
+ gr.Markdown("")
 
473
 
474
  with gr.Row():
475
  with gr.Column(scale=1):
476
+ btn_nb1 = gr.Button("Step 1: Data Creation", variant="secondary")
477
+ gr.Markdown("")
 
 
 
 
478
  with gr.Column(scale=1):
479
+ btn_nb2 = gr.Button("Step 2a: Python Analysis", variant="secondary")
480
+ gr.Markdown("")
 
 
 
 
481
  with gr.Column(scale=1):
482
+ btn_r = gr.Button("Step 2b: R Analysis", variant="secondary")
483
+ gr.Markdown("")
 
 
 
 
484
 
485
  with gr.Row():
486
+ btn_all = gr.Button("Run All 3 Steps", variant="primary")
 
 
 
487
 
488
  run_log = gr.Textbox(
489
  label="Execution Log",
 
548
  + (
549
  "*LLM is active.*"
550
  if LLM_ENABLED
551
+ else "*No API key detected — using keyword matching. "
552
  "Set `HF_API_KEY` in Space secrets for full LLM support.*"
553
  )
554
  )
555
 
556
  with gr.Row(equal_height=True):
557
  with gr.Column(scale=1):
558
+ chatbot = gr.Chatbot(label="Conversation", height=380)
 
 
 
559
  user_input = gr.Textbox(
560
  label="Ask about your data",
561
  placeholder="e.g. Show me sales trends / What drives revenue? / Compare forecast models",
 
574
  )
575
 
576
  with gr.Column(scale=1):
577
+ ai_figure = gr.Image(label="Visualisation", height=350)
578
+ ai_table = gr.Dataframe(label="Data Table", interactive=False)
 
 
 
 
 
 
579
 
580
  user_input.submit(
581
  ai_chat,
 
584
  )
585
 
586
  demo.launch(allowed_paths=[str(BASE_DIR)])