isabelle0629 commited on
Commit
6b102ce
·
verified ·
1 Parent(s): 5e05b80

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +25 -12
  2. app.py +53 -30
  3. requirements.txt +4 -4
README.md CHANGED
@@ -1,17 +1,19 @@
1
- # StreamSmart Recommender - Hugging Face Space
2
 
3
  Files included:
4
  - `app.py` - main Gradio app for Hugging Face Space
5
  - `requirements.txt` - Python dependencies
6
- - `n8n_streamsmart_workflow.json` - importable n8n workflow
 
7
 
8
  ## 1) Hugging Face setup
9
  Create a new **Gradio Space** and replace the default files with:
10
  - `app.py`
11
  - `requirements.txt`
12
 
13
- Optional secret:
14
- - `N8N_WEBHOOK_URL` = your production n8n webhook URL
 
15
 
16
  ## 2) CSV formats
17
  ### Reviews CSV required columns
@@ -36,15 +38,26 @@ Rates should be decimals such as `0.82`, not percentages like `82%`.
36
 
37
  ## 3) n8n setup
38
  In n8n:
39
- 1. Import `n8n_streamsmart_workflow.json`
40
- 2. Open the Webhook node and copy the **Production URL**
41
- 3. In Hugging Face Space settings, add a secret named `N8N_WEBHOOK_URL`
42
- 4. Rebuild the Space
43
- 5. Run the analysis in the app, then click `Send Top Recommendations to n8n`
44
-
45
- ## 4) What the app does
 
 
 
 
 
 
 
 
 
 
 
46
  - scores review sentiment with VADER
47
  - merges title-level sentiment with watch metrics
48
  - computes a weighted recommendation score
49
  - labels each title with a business action
50
- - sends the scored results to one n8n workflow
 
1
+ # StreamSmart Recommender - Hugging Face Space with 2 n8n workflows
2
 
3
  Files included:
4
  - `app.py` - main Gradio app for Hugging Face Space
5
  - `requirements.txt` - Python dependencies
6
+ - `n8n_processing_workflow.json` - import into n8n for full-result intake
7
+ - `n8n_report_workflow.json` - import into n8n for top-recommendation reporting
8
 
9
  ## 1) Hugging Face setup
10
  Create a new **Gradio Space** and replace the default files with:
11
  - `app.py`
12
  - `requirements.txt`
13
 
14
+ Add these Space secrets:
15
+ - `N8N_PROCESS_WEBHOOK_URL` = your production URL from the processing workflow
16
+ - `N8N_REPORT_WEBHOOK_URL` = your production URL from the report workflow
17
 
18
  ## 2) CSV formats
19
  ### Reviews CSV required columns
 
38
 
39
  ## 3) n8n setup
40
  In n8n:
41
+ 1. Import `n8n_processing_workflow.json`
42
+ 2. Import `n8n_report_workflow.json`
43
+ 3. Open each Webhook node and copy the **Production URL**
44
+ 4. Paste those URLs into the two Hugging Face Space secrets above
45
+ 5. Rebuild the Space
46
+
47
+ ## 4) What each workflow does
48
+ ### Processing workflow
49
+ - receives the full scored dataset from the app
50
+ - counts how many titles fall into each action category
51
+ - returns the top title and a short status message
52
+
53
+ ### Report workflow
54
+ - receives the top recommendations and all results
55
+ - computes average recommendation score by genre
56
+ - returns a text summary you can show in the app
57
+
58
+ ## 5) What the app does
59
  - scores review sentiment with VADER
60
  - merges title-level sentiment with watch metrics
61
  - computes a weighted recommendation score
62
  - labels each title with a business action
63
+ - sends results to two separate n8n workflows
app.py CHANGED
@@ -1,7 +1,7 @@
1
  import os
2
  import io
3
  import json
4
- from typing import Optional, Tuple
5
 
6
  import numpy as np
7
  import pandas as pd
@@ -168,9 +168,6 @@ def run_analysis(reviews_file, watch_file, use_demo: bool):
168
  reviews["sentiment_score"] = reviews["review_text"].apply(compute_sentiment)
169
  reviews["sentiment_label"] = reviews["sentiment_score"].apply(sentiment_label)
170
 
171
- agg_dict = {
172
- "sentiment_score": ["mean", "count"],
173
- }
174
  if "genre" in reviews.columns:
175
  review_agg = reviews.groupby("title", as_index=False).agg(
176
  avg_sentiment=("sentiment_score", "mean"),
@@ -216,13 +213,12 @@ def run_analysis(reviews_file, watch_file, use_demo: bool):
216
  f"Average completion rate: {merged['completion_rate'].mean():.2f}"
217
  )
218
 
219
- top_table = merged[
220
- [
221
- "title", "genre", "avg_sentiment", "avg_watch_time", "completion_rate",
222
- "drop_off_rate", "rewatch_rate", "click_through_rate", "review_count",
223
- "recommendation_score", "action"
224
- ]
225
  ]
 
226
 
227
  top_plot = px.bar(
228
  merged.head(10),
@@ -283,26 +279,44 @@ def update_title_choices(payload: str):
283
  return gr.Dropdown(choices=choices, value=value)
284
 
285
 
286
- def send_to_n8n(payload: str):
287
- if not payload:
288
- raise gr.Error("Run the analysis first.")
289
-
290
- webhook_url = os.getenv("N8N_WEBHOOK_URL", "").strip()
291
  if not webhook_url:
292
- return (
293
- "N8N_WEBHOOK_URL is not set yet. Add it as a Hugging Face Space secret, then try again."
294
- )
295
 
296
- data = json.loads(payload)
297
- top5 = data[:5]
298
- response = requests.post(webhook_url, json={"app": APP_TITLE, "top_recommendations": top5, "all_results": data}, timeout=60)
299
  response.raise_for_status()
300
 
301
  try:
302
  result = response.json()
303
- return f"n8n workflow ran successfully. Response: {json.dumps(result, indent=2)}"
304
  except Exception:
305
- return f"n8n workflow ran successfully. Raw response: {response.text}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
 
307
 
308
  with gr.Blocks(title=APP_TITLE) as demo:
@@ -335,11 +349,14 @@ with gr.Blocks(title=APP_TITLE) as demo:
335
 
336
  with gr.Tab("4. n8n Automation"):
337
  gr.Markdown(
338
- "This button sends your scored results to one n8n workflow through a webhook. "
339
- "Set the `N8N_WEBHOOK_URL` secret in your Hugging Face Space first."
 
340
  )
341
- n8n_btn = gr.Button("Send Top Recommendations to n8n")
342
- n8n_status = gr.Textbox(label="n8n Status", lines=5)
 
 
343
 
344
  with gr.Tab("5. Download"):
345
  download_file = gr.File(label="Download processed CSV")
@@ -370,10 +387,16 @@ with gr.Blocks(title=APP_TITLE) as demo:
370
  outputs=[detail_box],
371
  )
372
 
373
- n8n_btn.click(
374
- fn=send_to_n8n,
 
 
 
 
 
 
375
  inputs=[payload_state],
376
- outputs=[n8n_status],
377
  )
378
 
379
  if __name__ == "__main__":
 
1
  import os
2
  import io
3
  import json
4
+ from typing import Tuple
5
 
6
  import numpy as np
7
  import pandas as pd
 
168
  reviews["sentiment_score"] = reviews["review_text"].apply(compute_sentiment)
169
  reviews["sentiment_label"] = reviews["sentiment_score"].apply(sentiment_label)
170
 
 
 
 
171
  if "genre" in reviews.columns:
172
  review_agg = reviews.groupby("title", as_index=False).agg(
173
  avg_sentiment=("sentiment_score", "mean"),
 
213
  f"Average completion rate: {merged['completion_rate'].mean():.2f}"
214
  )
215
 
216
+ table_cols = [
217
+ "title", "genre", "avg_sentiment", "avg_watch_time", "completion_rate",
218
+ "drop_off_rate", "rewatch_rate", "click_through_rate", "review_count",
219
+ "recommendation_score", "action"
 
 
220
  ]
221
+ top_table = merged[table_cols]
222
 
223
  top_plot = px.bar(
224
  merged.head(10),
 
279
  return gr.Dropdown(choices=choices, value=value)
280
 
281
 
282
+ def _post_to_webhook(env_name: str, body: dict, success_label: str) -> str:
283
+ webhook_url = os.getenv(env_name, "").strip()
 
 
 
284
  if not webhook_url:
285
+ return f"{env_name} is not set yet. Add it as a Hugging Face Space secret, then try again."
 
 
286
 
287
+ response = requests.post(webhook_url, json=body, timeout=60)
 
 
288
  response.raise_for_status()
289
 
290
  try:
291
  result = response.json()
292
+ return f"{success_label} ran successfully. Response: {json.dumps(result, indent=2)}"
293
  except Exception:
294
+ return f"{success_label} ran successfully. Raw response: {response.text}"
295
+
296
+
297
+ def send_to_processing_workflow(payload: str):
298
+ if not payload:
299
+ raise gr.Error("Run the analysis first.")
300
+
301
+ data = json.loads(payload)
302
+ return _post_to_webhook(
303
+ "N8N_PROCESS_WEBHOOK_URL",
304
+ {"app": APP_TITLE, "records": data, "record_count": len(data)},
305
+ "n8n processing workflow",
306
+ )
307
+
308
+
309
+ def send_to_report_workflow(payload: str):
310
+ if not payload:
311
+ raise gr.Error("Run the analysis first.")
312
+
313
+ data = json.loads(payload)
314
+ top5 = data[:5]
315
+ return _post_to_webhook(
316
+ "N8N_REPORT_WEBHOOK_URL",
317
+ {"app": APP_TITLE, "top_recommendations": top5, "all_results": data},
318
+ "n8n report workflow",
319
+ )
320
 
321
 
322
  with gr.Blocks(title=APP_TITLE) as demo:
 
349
 
350
  with gr.Tab("4. n8n Automation"):
351
  gr.Markdown(
352
+ "Set these Hugging Face Space secrets before using the buttons below:\n\n"
353
+ "- `N8N_PROCESS_WEBHOOK_URL`\n"
354
+ "- `N8N_REPORT_WEBHOOK_URL`"
355
  )
356
+ process_btn = gr.Button("Send Full Results to n8n Processing Workflow")
357
+ process_status = gr.Textbox(label="Processing Workflow Status", lines=5)
358
+ report_btn = gr.Button("Send Top Recommendations to n8n Report Workflow")
359
+ report_status = gr.Textbox(label="Report Workflow Status", lines=5)
360
 
361
  with gr.Tab("5. Download"):
362
  download_file = gr.File(label="Download processed CSV")
 
387
  outputs=[detail_box],
388
  )
389
 
390
+ process_btn.click(
391
+ fn=send_to_processing_workflow,
392
+ inputs=[payload_state],
393
+ outputs=[process_status],
394
+ )
395
+
396
+ report_btn.click(
397
+ fn=send_to_report_workflow,
398
  inputs=[payload_state],
399
+ outputs=[report_status],
400
  )
401
 
402
  if __name__ == "__main__":
requirements.txt CHANGED
@@ -1,6 +1,6 @@
1
  gradio>=4.44.0
2
- pandas>=2.2.2
3
- numpy>=1.26.4
4
- plotly>=5.24.1
5
- requests>=2.32.3
6
  vaderSentiment>=3.3.2
 
1
  gradio>=4.44.0
2
+ pandas>=2.2.0
3
+ numpy>=1.26.0
4
+ plotly>=5.22.0
5
+ requests>=2.31.0
6
  vaderSentiment>=3.3.2