milindkamat0507 commited on
Commit
bea86ed
·
verified ·
1 Parent(s): 585f15c

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -296
app.py DELETED
@@ -1,296 +0,0 @@
1
- """
2
- app.py — BERTopic Topic Modelling Agent UI.
3
-
4
- Three UX features:
5
- 1. Phase banner — large prominent display of current B&C phase
6
- 2. Dynamic prompts — phase-appropriate suggested next actions
7
- 3. Auto-populated review table — loads from tool checkpoint files
8
-
9
- 9-column review table: #, Topic Label, Top Evidence, Sentences, Papers,
10
- Approve, Rename To, Move To, Reasoning.
11
- """
12
-
13
- import gradio as gr
14
- import pandas as pd
15
- import json
16
- import os
17
- import re
18
- import tempfile
19
- from datetime import datetime
20
- from pathlib import Path
21
- from agent import run as agent_run
22
-
23
- THREAD_ID = f"bertopic-{datetime.now().strftime('%Y%m%d%H%M%S')}"
24
-
25
- REVIEW_COLS = [
26
- "#", "Topic Label", "Top Evidence", "Sentences", "Papers",
27
- "Approve", "Rename To", "Move To", "Reasoning",
28
- ]
29
-
30
- EMPTY_TABLE = pd.DataFrame(columns=REVIEW_COLS)
31
-
32
- PHASE_INFO = {
33
- 0: ("Getting started", "⬜⬜⬜⬜⬜⬜",
34
- "Upload a CSV file, then click **Analyze my Scopus CSV** and press Send"),
35
- 1: ("Phase 1 — Familiarisation", "🟦⬜⬜⬜⬜⬜",
36
- "Click **Run abstract analysis** or **Run title analysis** and press Send"),
37
- 2: ("Phase 2 — Initial Codes", "🟦🟦⬜⬜⬜⬜",
38
- "Review clusters in the **Results table** below. Edit Approve / Rename / Move, "
39
- "then click **Submit Review**"),
40
- 3: ("Phase 3 — Themes", "🟦🟦🟦⬜⬜⬜",
41
- "Review merged themes. Edit the table, then click **Submit Review**"),
42
- 4: ("Phase 4 — Saturation", "🟦🟦🟦🟦⬜⬜",
43
- "Review saturation metrics. Click **Submit Review** to confirm"),
44
- 5: ("Phase 5 — Naming", "🟦🟦🟦🟦🟦⬜",
45
- "Review theme profiles. Edit names, then **Submit Review**"),
46
- 6: ("Phase 6 — Report", "🟦🟦🟦🟦🟦🟦",
47
- "Review comparison and narrative. **Submit Review** to finalise"),
48
- }
49
-
50
- _path = lambda file: str(
51
- (hasattr(file, "name") and file.name)
52
- or (isinstance(file, str) and file)
53
- or ""
54
- )
55
- _name = lambda file: os.path.basename(_path(file))
56
-
57
-
58
- def _extract_phase(text: str) -> int:
59
- """Extract phase number from agent response. Returns 0 if not found."""
60
- found = re.findall(r"Phase (\d)", str(text))
61
- return int((found or ["0"])[0])
62
-
63
-
64
- def _phase_banner(num: int) -> str:
65
- """Generate prominent phase banner with progress bar and next step."""
66
- name, progress, instruction = PHASE_INFO.get(num, PHASE_INFO[0])
67
- return (
68
- f"## {progress} {name}\n\n"
69
- f"**NEXT STEP →** {instruction}"
70
- )
71
-
72
-
73
- def _load_review_table(base_dir: str) -> pd.DataFrame:
74
- """Load latest checkpoint file into the 9-column review table.
75
-
76
- Scans base_dir for topic_labels.json, themes.json, taxonomy_alignment.json,
77
- summaries.json. Loads the most recently modified one and formats it.
78
- Returns EMPTY_TABLE if nothing found.
79
- """
80
- base = Path(str(base_dir or "/tmp/nonexistent_dir_placeholder"))
81
- candidates = (
82
- base_dir and base.exists() and sorted(
83
- (
84
- list(base.glob("topic_labels.json"))
85
- + list(base.glob("themes.json"))
86
- + list(base.glob("taxonomy_alignment.json"))
87
- + list(base.glob("summaries.json"))
88
- ),
89
- key=lambda p: p.stat().st_mtime,
90
- reverse=True,
91
- )
92
- ) or []
93
-
94
- latest = (candidates[:1] or [None])[0]
95
- return (latest and [_format_checkpoint(latest)] or [EMPTY_TABLE.copy()])[0]
96
-
97
-
98
- def _format_checkpoint(path) -> pd.DataFrame:
99
- """Format a checkpoint JSON file into review table rows."""
100
- raw = json.loads(Path(path).read_text())
101
- data = (isinstance(raw, dict) and raw.get("clusters", raw.get("per_theme", []))) or \
102
- (isinstance(raw, list) and raw) or []
103
-
104
- def _row(item: dict) -> dict:
105
- """Map one JSON item to review table columns."""
106
- return {
107
- "#": item.get("topic_id", item.get("theme_id", 0)),
108
- "Topic Label": item.get("label", item.get("theme_label", "")),
109
- "Top Evidence": str(item.get("representative",
110
- item.get("notes", "")))[:150],
111
- "Sentences": item.get("size", item.get("total_papers",
112
- item.get("papers", 0))),
113
- "Papers": item.get("size", item.get("total_papers",
114
- item.get("papers", 0))),
115
- "Approve": "",
116
- "Rename To": "",
117
- "Move To": "",
118
- "Reasoning": str(item.get("rationale", item.get("notes", ""))),
119
- }
120
-
121
- rows = list(map(_row, data[:50]))
122
- return (rows and [pd.DataFrame(rows, columns=REVIEW_COLS)] or [EMPTY_TABLE.copy()])[0]
123
-
124
-
125
- def on_file_upload(file):
126
- """Extract CSV stats and store base directory."""
127
- path = _path(file)
128
- result = (not path) and ("Upload a CSV to begin.", "", _phase_banner(0))
129
- return result or _do_file_upload(path, file)
130
-
131
-
132
- def _do_file_upload(path: str, file) -> tuple:
133
- """Actual file processing after path validation."""
134
- df = pd.read_csv(path)
135
- rows, cols = df.shape
136
- base = str(Path(path).parent)
137
- info = (
138
- f"**Loaded:** `{_name(file)}`\n\n"
139
- f"**Shape:** {rows:,} rows x {cols} columns\n\n"
140
- f"**Columns:** {', '.join(df.columns[:6].tolist())}\n\n"
141
- f"*Click a prompt below and press Send to begin.*"
142
- )
143
- return info, base, _phase_banner(1)
144
-
145
-
146
- def on_send(user_msg, history, file, base_dir):
147
- """Pass user message to agent. Update phase banner and review table."""
148
- msg = (user_msg or "").strip() or "help"
149
- csv_tag = f"[CSV: {_path(file)}]\n" * bool(file)
150
-
151
- history = list(history or [])
152
- history.append({"role": "user", "content": msg})
153
- history.append({"role": "assistant", "content": "Thinking..."})
154
- yield history, "", gr.skip(), gr.skip(), gr.skip()
155
-
156
- reply = agent_run(csv_tag + msg, thread_id=THREAD_ID)
157
- history[-1] = {"role": "assistant", "content": reply}
158
-
159
- phase = _extract_phase(reply)
160
- banner = _phase_banner(phase)
161
- table = _load_review_table(base_dir)
162
-
163
- yield history, "", banner, table, base_dir
164
-
165
-
166
- def on_submit_review(table_df, history, base_dir):
167
- """Serialise review table edits to agent."""
168
- history = list(history or [])
169
- edits = table_df.to_json(orient="records", indent=2)
170
-
171
- history.append({"role": "user", "content": "[REVIEW SUBMITTED]"})
172
- history.append({"role": "assistant", "content": "Processing review..."})
173
-
174
- reply = agent_run(
175
- "Reviewer submitted table edits.\n\n"
176
- f"```json\n{edits}\n```\n\n"
177
- "Process: Approve/Reject decisions, Rename To values, "
178
- "Move To reassignments (call reassign_sentences if moves exist), "
179
- "Reasoning notes. Then check STOP gates and proceed.",
180
- thread_id=THREAD_ID,
181
- )
182
- history[-1] = {"role": "assistant", "content": reply}
183
-
184
- phase = _extract_phase(reply)
185
- return history, _phase_banner(phase), _load_review_table(base_dir)
186
-
187
-
188
- def on_download(table_df, history):
189
- """Export review CSV and chat TXT."""
190
- csv_tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".csv", prefix="review_")
191
- table_df.to_csv(csv_tmp.name, index=False)
192
-
193
- txt_tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".txt", prefix="chat_")
194
- txt_tmp.write(
195
- "\n\n".join(
196
- list(map(
197
- lambda m: f"{m.get('role', '').upper()}: {m.get('content', '')}",
198
- history or [],
199
- ))
200
- ).encode("utf-8")
201
- )
202
- txt_tmp.close()
203
- return [csv_tmp.name, txt_tmp.name]
204
-
205
-
206
- with gr.Blocks(title="BERTopic Agent") as demo:
207
-
208
- base_dir_state = gr.State(value="")
209
-
210
- gr.Markdown("# BERTopic Modelling Agent")
211
- gr.Markdown(
212
- "**Braun & Clarke 6-Phase Thematic Analysis** "
213
- "| 10 Tools | 6 STOP Gates | Cosine Agglomerative Clustering"
214
- )
215
-
216
- phase_banner = gr.Markdown(value=_phase_banner(0))
217
-
218
- gr.Markdown("---\n### Section 1 — Data input")
219
- with gr.Row():
220
- with gr.Column(scale=3):
221
- file_input = gr.File(
222
- label="Upload Scopus CSV",
223
- file_types=[".csv"],
224
- file_count="single",
225
- )
226
- with gr.Column(scale=5):
227
- file_info = gr.Markdown("Upload a CSV to begin.")
228
-
229
- gr.Markdown("---\n### Section 2 — Agent conversation")
230
- chatbot = gr.Chatbot(label="BERTopic Agent", height=200)
231
- with gr.Row():
232
- msg_box = gr.Textbox(
233
- placeholder="Type a message or click a prompt below, then press Send",
234
- show_label=False, scale=7, lines=1,
235
- )
236
- send_btn = gr.Button("Send", variant="primary", scale=1)
237
-
238
- gr.Examples(
239
- examples=[
240
- "Analyze my Scopus CSV",
241
- "Run abstract analysis",
242
- "Run title analysis",
243
- "Proceed to next phase",
244
- "Show corpus statistics",
245
- ],
246
- inputs=msg_box,
247
- label="Quick prompts (click to fill, then press Send)",
248
- )
249
-
250
- gr.Markdown("---\n### Section 3 — Results (auto-populated from tool outputs)")
251
- gr.Markdown(
252
- "This table fills automatically when the agent runs tools. "
253
- "Edit **Approve**, **Rename To**, **Move To**, **Reasoning** columns, "
254
- "then click **Submit Review**."
255
- )
256
- review_table = gr.Dataframe(
257
- value=EMPTY_TABLE,
258
- headers=REVIEW_COLS,
259
- datatype=["number", "str", "str", "number", "number",
260
- "str", "str", "str", "str"],
261
- column_count=(9, "fixed"),
262
- interactive=True, wrap=True,
263
- )
264
- with gr.Row():
265
- clear_btn = gr.Button("Clear table", variant="secondary", scale=2)
266
- sub_btn = gr.Button("Submit Review", variant="primary", scale=4)
267
-
268
- with gr.Accordion("Download", open=False):
269
- dl_btn = gr.Button("Generate downloads", variant="primary")
270
- dl_files = gr.File(label="Downloads", file_count="multiple",
271
- interactive=False)
272
-
273
- file_input.change(
274
- on_file_upload,
275
- inputs=[file_input],
276
- outputs=[file_info, base_dir_state, phase_banner],
277
- )
278
- send_btn.click(
279
- on_send,
280
- inputs=[msg_box, chatbot, file_input, base_dir_state],
281
- outputs=[chatbot, msg_box, phase_banner, review_table, base_dir_state],
282
- )
283
- msg_box.submit(
284
- on_send,
285
- inputs=[msg_box, chatbot, file_input, base_dir_state],
286
- outputs=[chatbot, msg_box, phase_banner, review_table, base_dir_state],
287
- )
288
- clear_btn.click(lambda: EMPTY_TABLE.copy(), outputs=[review_table])
289
- sub_btn.click(
290
- on_submit_review,
291
- inputs=[review_table, chatbot, base_dir_state],
292
- outputs=[chatbot, phase_banner, review_table],
293
- )
294
- dl_btn.click(on_download, inputs=[review_table, chatbot], outputs=[dl_files])
295
-
296
- demo.launch(ssr_mode=False, theme=gr.themes.Soft())