Skip to content

Commit b194018

Browse files
yanomalysamjulien
authored andcommitted
HN Listener UI fixes (#10)
- Removed backend driven UI usage and replaced it by Repeater component - Removed messages visibility flags - Fixed validation of scrapped data
1 parent bff4edf commit b194018

4 files changed

Lines changed: 73 additions & 49 deletions

File tree

hacker-news-social-listener/.wf/components-page-0-c0f99a9e-5004-4e75-a6c6-36f17490b134.jsonl

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{"id": "m7luxumzscv65i09", "type": "tabs", "content": {}, "handlers": {}, "isCodeManaged": false, "parentId": "c0f99a9e-5004-4e75-a6c6-36f17490b134", "position": 1, "visible": {"binding": "", "expression": true, "reversed": false}}
44
{"id": "lon4vs20gd3myh7e", "type": "tab", "content": {"name": "Setup"}, "handlers": {}, "isCodeManaged": false, "parentId": "m7luxumzscv65i09", "position": 0, "visible": {"binding": "", "expression": true, "reversed": false}}
55
{"id": "xlc1wtcwu1g2i07p", "type": "heading", "content": {"text": "Navigate and analyze Hacker News posts and comments using Writer graph-based RAG and Palmyra X 004."}, "handlers": {}, "isCodeManaged": false, "parentId": "lon4vs20gd3myh7e", "position": 0}
6-
{"id": "62r9auy895datfnp", "type": "message", "content": {"message": "@{message_setup}"}, "handlers": {}, "isCodeManaged": false, "parentId": "lon4vs20gd3myh7e", "position": 1, "visible": {"binding": "message_setup_vis", "expression": "custom", "reversed": false}}
6+
{"id": "62r9auy895datfnp", "type": "message", "content": {"message": "@{message_setup}"}, "handlers": {}, "isCodeManaged": false, "parentId": "lon4vs20gd3myh7e", "position": 1, "visible": {"binding": "message_setup_vis", "expression": true, "reversed": false}}
77
{"id": "mmh30t1tiyv5mi9s", "type": "section", "content": {"containerBackgroundColor": "#BFCBFF", "isCollapsible": "yes", "title": "\ud83d\udd0e Pull latest N posts"}, "handlers": {}, "isCodeManaged": false, "parentId": "lon4vs20gd3myh7e", "position": 2}
88
{"id": "hpeff7ha6v27zsk8", "type": "text", "content": {"text": "**Pull up to 500 Hacker News stories, with or without comments.** Data from the Hacker News API is sent to to Writer\u2019s graph-based RAG system, known as a Knowledge Graph. This is accessed dynamically by an LLM through the Writer RAG tool.", "useMarkdown": "yes"}, "handlers": {}, "isCodeManaged": false, "parentId": "mmh30t1tiyv5mi9s", "position": 0}
99
{"id": "up5ofq0drv233umy", "type": "section", "content": {"containerBackgroundColor": "#FFD8CD", "isCollapsible": "yes", "title": "\ud83d\ude4b\u200d\u2640\ufe0f Ask questions"}, "handlers": {}, "isCodeManaged": false, "parentId": "lon4vs20gd3myh7e", "position": 3}
@@ -25,14 +25,19 @@
2525
{"id": "crm2bdbrjclid4k4", "type": "chatbot", "content": {"conversation": "@{conversation}", "useMarkdown": "yes"}, "handlers": {"wf-chatbot-message": "message_handler"}, "isCodeManaged": false, "parentId": "rosw32keaejygiir", "position": 0}
2626
{"id": "814lb9dktd2e1ye3", "type": "horizontalstack", "content": {"contentHAlign": "start"}, "handlers": {}, "isCodeManaged": false, "parentId": "rosw32keaejygiir", "position": 1}
2727
{"id": "t171dwz5muor2n9x", "type": "text", "content": {"alignment": "center", "text": "@{contributing_sources_button_text}", "useMarkdown": "yes"}, "handlers": {"wf-click": "contributing_sources_change_vis"}, "isCodeManaged": false, "parentId": "814lb9dktd2e1ye3", "position": 0}
28-
{"id": "contributed_sources", "type": "column", "content": {"cssClasses": "files-list", "title": "Contributing sources", "width": "7"}, "handlers": {}, "isCodeManaged": false, "parentId": "rpi88dvxmlxr0qd9", "position": 1, "visible": {"binding": "contributing_sources_vis", "expression": "custom", "reversed": false}}
28+
{"id": "lcjq00s0z6a4cv6f", "type": "column", "content": {"title": "Contributing sources", "width": "7"}, "handlers": {}, "isCodeManaged": false, "parentId": "rpi88dvxmlxr0qd9", "position": 1, "visible": {"binding": "contributing_sources_vis", "expression": "custom", "reversed": false}}
29+
{"id": "pn1m5c8qgt9s24a8", "type": "columns", "content": {}, "handlers": {}, "isCodeManaged": false, "parentId": "lcjq00s0z6a4cv6f", "position": 0}
30+
{"id": "contributed_sources", "type": "column", "content": {"cssClasses": "files-list", "title": "", "width": "7"}, "handlers": {}, "isCodeManaged": false, "parentId": "pn1m5c8qgt9s24a8", "position": 0, "visible": {"binding": "contributing_sources_vis", "expression": "custom", "reversed": false}}
31+
{"id": "vncphxzffznqjhvv", "type": "repeater", "content": {"keyVariable": "index", "repeaterObject": "@{contributing_sources}", "valueVariable": "file"}, "handlers": {}, "isCodeManaged": false, "parentId": "contributed_sources", "position": 0}
32+
{"id": "e1s5oy7xr2v7jlas", "type": "section", "content": {"cssClasses": "@{file.file_css}", "title": "@{file.name}"}, "handlers": {}, "isCodeManaged": false, "parentId": "vncphxzffznqjhvv", "position": 0}
33+
{"id": "01fzo8tvm1dop7us", "type": "text", "content": {"cssClasses": "@{file.content_css}", "text": "@{file.content}", "useMarkdown": "yes"}, "handlers": {}, "isCodeManaged": false, "parentId": "e1s5oy7xr2v7jlas", "position": 0}
2934
{"id": "erkqharzgysk5s3n", "type": "tab", "content": {"name": "Generate trend report"}, "handlers": {}, "isCodeManaged": false, "parentId": "m7luxumzscv65i09", "position": 3, "visible": {"binding": "", "expression": true, "reversed": false}}
30-
{"id": "vzbrrx0dlp3skcn2", "type": "message", "content": {"message": "@{message_report}"}, "handlers": {}, "isCodeManaged": false, "parentId": "erkqharzgysk5s3n", "position": 0, "visible": {"binding": "message_report_vis", "expression": "custom", "reversed": false}}
35+
{"id": "vzbrrx0dlp3skcn2", "type": "message", "content": {"message": "@{message_report}"}, "handlers": {}, "isCodeManaged": false, "parentId": "erkqharzgysk5s3n", "position": 0, "visible": {"binding": "message_report_vis", "expression": true, "reversed": false}}
3136
{"id": "osvuzxnivfs3qrut", "type": "text", "content": {"text": "@{prepared_report}", "useMarkdown": "yes"}, "handlers": {}, "isCodeManaged": false, "parentId": "erkqharzgysk5s3n", "position": 1}
3237
{"id": "w2hg99rv80b12tpt", "type": "button", "content": {"text": "Generate report"}, "handlers": {"wf-click": "run_report"}, "isCodeManaged": false, "parentId": "erkqharzgysk5s3n", "position": 2, "visible": {"binding": "", "expression": true, "reversed": false}}
33-
{"id": "so0q29s1ql8bsdj5", "type": "horizontalstack", "content": {}, "handlers": {}, "parentId": "c0f99a9e-5004-4e75-a6c6-36f17490b134", "position": 2}
34-
{"id": "n40gsxjweizjl009", "type": "link", "content": {"rel": "noopener noreferrer", "target": "_blank", "text": "Made with Writer Framework.", "url": "https://github.com/writer/writer-framework"}, "handlers": {}, "parentId": "so0q29s1ql8bsdj5", "position": 0}
35-
{"id": "s1sfmjca2cc9q0ho", "type": "text", "content": {"text": "|"}, "handlers": {}, "parentId": "so0q29s1ql8bsdj5", "position": 1}
38+
{"id": "so0q29s1ql8bsdj5", "type": "horizontalstack", "content": {}, "handlers": {}, "isCodeManaged": false, "parentId": "c0f99a9e-5004-4e75-a6c6-36f17490b134", "position": 2}
39+
{"id": "n40gsxjweizjl009", "type": "link", "content": {"rel": "noopener noreferrer", "target": "_blank", "text": "Made with Writer Framework.", "url": "https://github.com/writer/writer-framework"}, "handlers": {}, "isCodeManaged": false, "parentId": "so0q29s1ql8bsdj5", "position": 0}
40+
{"id": "s1sfmjca2cc9q0ho", "type": "text", "content": {"text": "|"}, "handlers": {}, "isCodeManaged": false, "parentId": "so0q29s1ql8bsdj5", "position": 1}
3641
{"id": "7a41wj8am7nk74we", "type": "link", "content": {"rel": "noopener noreferrer", "target": "_blank", "text": "Build your own RAG app.", "url": "https://writer.com/engineering/rag-tool/"}, "handlers": {}, "isCodeManaged": false, "parentId": "so0q29s1ql8bsdj5", "position": 2}
37-
{"id": "bxi8e7e2yexuifxo", "type": "text", "content": {"text": "|"}, "handlers": {}, "parentId": "so0q29s1ql8bsdj5", "position": 3}
38-
{"id": "0pjs3f2feqnik6oh", "type": "link", "content": {"rel": "noopener noreferrer", "target": "_blank", "text": "Get the code.", "url": "https://github.com/writer/framework-tutorials/tree/main/hacker-news-social-listener"}, "handlers": {}, "parentId": "so0q29s1ql8bsdj5", "position": 4}
42+
{"id": "bxi8e7e2yexuifxo", "type": "text", "content": {"text": "|"}, "handlers": {}, "isCodeManaged": false, "parentId": "so0q29s1ql8bsdj5", "position": 3}
43+
{"id": "0pjs3f2feqnik6oh", "type": "link", "content": {"rel": "noopener noreferrer", "target": "_blank", "text": "Get the code.", "url": "https://github.com/writer/framework-tutorials/tree/main/hacker-news-social-listener"}, "handlers": {}, "isCodeManaged": false, "parentId": "so0q29s1ql8bsdj5", "position": 4}

hacker-news-social-listener/main.py

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -33,28 +33,45 @@
3333
def main(state: WriterState) -> None:
3434
_delete_files_from_graph(GRAPH_ID)
3535
state["message_setup"] = "%Scraping data"
36-
state["message_setup_vis"] = True
3736

3837
posts, comments = _scrape_hackernews(state)
3938
state["message_setup"] = "%Data was scraped"
40-
state["posts"] = posts[["title", "created_utc", "score", "num_comments", "url"]] if posts is not None else pd.DataFrame()
39+
posts_columns = ["title", "created_utc", "score", "num_comments", "url"]
40+
state["posts"] = (
41+
posts[posts_columns]
42+
if _verify_df(posts, posts_columns)
43+
else pd.DataFrame(data={"Info": ["Invalid data was scrapped"]})
44+
)
4145
if state["allow_comments"]:
42-
state["comments"] = comments[["body", "author", "created_utc"]] if comments is not None else pd.DataFrame()
46+
comments_columns = ["body", "author", "created_utc"]
47+
state["comments"] = (
48+
comments[comments_columns]
49+
if _verify_df(comments, comments_columns)
50+
else pd.DataFrame(data={"Info": ["Invalid data was scrapped"]})
51+
)
4352
state["message_setup"] = "%Scraped data, now saving to csv"
4453

4554
_save_results_to_csv(state)
4655
state["message_setup"] = "%Saved data, now uploading to KG"
4756

4857
files_path = "static/hackernews_posts.csv"
49-
_upload_file_and_add_to_graph(files_path, GRAPH_ID)
58+
_upload_file_and_add_to_graph(state, files_path, GRAPH_ID)
5059
state["message_setup"] = "%Uploaded file and added to graph"
5160

5261
if state["allow_comments"]:
5362
file_path = "static/hackernews_comments.csv"
54-
_upload_file_and_add_to_graph(file_path, GRAPH_ID)
63+
_upload_file_and_add_to_graph(state, file_path, GRAPH_ID)
64+
65+
state["message_setup"] = ""
5566

56-
state["message_setup"] = "+Scrapping is completed!"
57-
state["message_setup_vis"] = False
67+
68+
def _verify_df(df: pd.DataFrame, columns: List) -> bool:
69+
if df is None:
70+
return False
71+
elif not all(column in df.columns for column in columns):
72+
return False
73+
else:
74+
return True
5875

5976

6077
def _delete_files_from_graph(graph_id: str) -> None:
@@ -82,8 +99,8 @@ def _get_file_from_graph(file_id: str) -> Optional[File]:
8299

83100
def _scrape_hackernews(state: WriterState) -> tuple[Any, Any]:
84101
stories_ids = _get_stories_ids(state)
85-
posts, comments_ids = _get_posts(stories_ids)
86-
comments = _get_comments(comments_ids)
102+
posts, comments_ids = _get_posts(state, stories_ids)
103+
comments = _get_comments(state, comments_ids)
87104

88105
if len(posts) > 0:
89106
state["posts"] = pd.DataFrame(posts).sort_values(
@@ -108,10 +125,11 @@ def _get_stories_ids(state: WriterState) -> List[str]:
108125
return response.json()[: int(state["fetch_limit"])]
109126
except Exception as e:
110127
print(f"Failed to fetch story ids from Hacker News: {str(e)}")
128+
state["message_setup"] = ""
111129
return []
112130

113131

114-
def _get_posts(stories_ids: List[str]) -> (List[dict], List[int]):
132+
def _get_posts(state: WriterState, stories_ids: List[str]) -> (List, List):
115133
try:
116134
stories_urls = [
117135
f"{HACKERNEWS_API_URL}/item/{story_id}.json" for story_id in stories_ids
@@ -143,10 +161,11 @@ def _get_posts(stories_ids: List[str]) -> (List[dict], List[int]):
143161
return posts_data, comments_ids
144162
except Exception as e:
145163
print(f"Failed to fetch stories from Hacker News: {str(e)}")
146-
return ([], [])
164+
state["message_setup"] = ""
165+
return [], []
147166

148167

149-
def _get_comments(comments_ids: List[str]) -> List[dict]:
168+
def _get_comments(state: WriterState, comments_ids: List[str]) -> List[dict]:
150169
try:
151170
comments_urls = [
152171
f"{HACKERNEWS_API_URL}/item/{comment_id}.json"
@@ -172,6 +191,7 @@ def _get_comments(comments_ids: List[str]) -> List[dict]:
172191
return comments_data
173192
except Exception as e:
174193
print(f"Failed to fetch comments from Hacker News: {str(e)}")
194+
state["message_setup"] = ""
175195
return []
176196

177197

@@ -194,14 +214,17 @@ def _save_results_to_csv(state: WriterState) -> None:
194214
state["comments"].to_csv("static/hackernews_comments.csv", index=False)
195215

196216

197-
def _upload_file_and_add_to_graph(file_path: str, graph_id: str) -> dict:
217+
def _upload_file_and_add_to_graph(
218+
state: WriterState, file_path: str, graph_id: str
219+
) -> dict:
198220
try:
199221
file_id = _upload_file(file_path)
200222
_add_file_to_graph(graph_id, file_id)
201223

202224
return {"file_id": file_id, "graph_id": graph_id}
203225
except Exception as e:
204226
print(f"An error while file uploading occurred: {str(e)}")
227+
state["message_setup"] = ""
205228
return {}
206229

207230

@@ -220,30 +243,31 @@ def _add_file_to_graph(graph_id: str, file_id: str) -> None:
220243

221244
def _handle_contributing_sources(state: WriterState, graph_data: dict) -> None:
222245
sources = graph_data.get("sources")
246+
contributing_sources = {}
223247
if sources:
224-
with wf.init_ui() as ui:
225-
with ui.refresh_with("contributed_sources"):
226-
for index, source in enumerate(sources):
227-
source_file = _get_file_from_graph(source["file_id"])
228-
source_snippet = source["snippet"]
229-
ui.Section(
230-
content={
231-
"title": "📄 " + source_file.name,
232-
"cssClasses": "file",
233-
},
234-
id=f"source {index}",
235-
)
236-
with ui.find(f"source {index}"):
237-
ui.Text({"text": source_snippet, "cssClasses": "file-text"})
248+
for index, source in enumerate(sources):
249+
source_file = _get_file_from_graph(source["file_id"])
250+
source_snippet = source["snippet"]
238251

252+
contributing_sources.update(
253+
{
254+
str(index): {
255+
"name": f"📄 {source_file.name}",
256+
"file_css": "file",
257+
"content": source_snippet,
258+
"content_css": "file-text",
259+
}
260+
}
261+
)
262+
263+
state["contributing_sources"] = contributing_sources
239264
state["contributing_sources_vis"] = True
240265
state["contributing_sources_button_text"] = "View contributing sources ▸"
241266

242267

243268
def run_report(state: WriterState) -> None:
244269
try:
245270
state["message_report"] = "%Creating report"
246-
state["message_report_vis"] = True
247271

248272
prompt = report_prompt(state["posts"], state["comments"])
249273
report_convo = Conversation()
@@ -255,10 +279,10 @@ def run_report(state: WriterState) -> None:
255279
for chunk in response:
256280
state["prepared_report"] += chunk["content"]
257281

258-
state["message_report"] = "+Creation is finished!"
259-
state["message_report_vis"] = False
282+
state["message_report"] = ""
260283
except Exception as e:
261284
state["prepared_report"] = "Something went wrong. Please try again!"
285+
state["message_report"] = ""
262286
raise e
263287

264288

@@ -304,9 +328,13 @@ def contributing_sources_change_vis(state: WriterState) -> None:
304328
{
305329
"conversation": Conversation(
306330
[
307-
{"role": "assistant", "content": "Ask me anything about the scraped Hacker News data."},
331+
{
332+
"role": "assistant",
333+
"content": "Ask me anything about the scraped Hacker News data.",
334+
},
308335
],
309336
),
337+
"contributing_sources": {},
310338
"response": None,
311339
"file_path": "",
312340
"graph_name": "",
@@ -316,14 +344,10 @@ def contributing_sources_change_vis(state: WriterState) -> None:
316344
"contributing_sources_button_text": "View contributing sources ◂",
317345
"message_setup": "",
318346
"message_report": "",
319-
"message_setup_vis": False,
320-
"message_report_vis": False,
321347
"contributing_sources_vis": False,
322348
"fetch_limit": 100,
323349
"allow_comments": True,
324350
}
325351
)
326352

327-
initial_state.import_frontend_module("scripts", "/static/custom.js")
328353
initial_state.import_stylesheet("style", "/static/custom.css")
329-
initial_state.call_frontend_function("scripts", "initSelectedDropdownOption", [])

hacker-news-social-listener/static/custom.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
}
99

1010
.files-list{
11-
height: 84vh !important;
11+
height: 79vh !important;
1212
overflow-y: auto !important;
1313
}
1414

hacker-news-social-listener/static/custom.js

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)