igortech commited on
Commit
bbbb62e
·
verified ·
1 Parent(s): 2c44f75

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -115
app.py CHANGED
@@ -1,13 +1,13 @@
1
  import json
2
- import random
3
- import re
4
- import string
5
  import os
6
  import datetime
 
 
7
  import gradio as gr
 
8
 
9
  # -----------------------------
10
- # Config / data loading
11
  # -----------------------------
12
  DATA_PATH = "quotes.json"
13
 
@@ -21,176 +21,141 @@ def load_quotes():
21
  return data
22
  except Exception as e:
23
  print(f"Failed to load {DATA_PATH}: {e}")
24
- print("No dataset file found. Upload one via the UI.")
25
  return {}
26
 
27
  QUOTES = load_quotes()
 
28
 
29
  # -----------------------------
30
- # Text helpers
31
- # -----------------------------
32
- STOPWORDS = {
33
- "the","a","an","and","or","but","if","then","so","than","to","of","in","on","at","for",
34
- "is","are","was","were","be","being","been","it","that","this","these","those","with",
35
- "as","by","from","about","into","over","after","before","up","down","out"
36
- }
37
-
38
- POS_HINTS = {"good","great","love","like","enjoy","awesome","amazing","nice","positive","best","fantastic","excellent"}
39
- NEG_HINTS = {"bad","hate","dislike","worst","awful","terrible","negative","poor","meh","gross","unsafe","hard","difficult"}
40
-
41
- punct_re = re.compile(f"[{re.escape(string.punctuation)}]")
42
-
43
- def normalize(text: str) -> str:
44
- return punct_re.sub(" ", (text or "").lower())
45
-
46
- def tokenize(text: str):
47
- return [t for t in normalize(text).split() if t and t not in STOPWORDS]
48
-
49
- def infer_sentiment(user_text: str) -> str:
50
- tl = normalize(user_text)
51
- has_pos = any(w in tl for w in POS_HINTS)
52
- has_neg = any(w in tl for w in NEG_HINTS)
53
- if has_pos and not has_neg:
54
- return "positive"
55
- if has_neg and not has_pos:
56
- return "negative"
57
- # tie or unknown → default to positive
58
- return "positive"
59
-
60
- # -----------------------------
61
- # Retrieval
62
  # -----------------------------
63
- def best_match_quote(category: str, sentiment: str, user_text: str) -> str:
64
- if category not in QUOTES:
65
- return f"No quotes found for category '{category}'."
66
- if sentiment not in QUOTES[category]:
67
- return f"No quotes found for sentiment '{sentiment}' in category '{category}'."
68
 
69
- pool = QUOTES[category][sentiment]
70
- if not pool:
71
- return f"No quotes available in '{category}' → '{sentiment}'."
72
-
73
- q_tokens = set(tokenize(user_text))
74
- best_score = -1
75
  best_quote = None
76
-
77
- for quote in pool:
78
- qtoks = set(tokenize(quote))
 
79
  score = len(q_tokens & qtoks)
80
  if score > best_score:
81
  best_score = score
82
  best_quote = quote
83
 
84
- if best_quote is None or best_score == 0:
85
- return random.choice(pool)
86
  return best_quote
87
 
88
- # -----------------------------
89
- # Gradio callbacks
90
- # -----------------------------
91
- def respond(message, history, category, sentiment_choice):
92
- if not QUOTES:
93
- bot = "No dataset loaded. Please upload a JSON file first."
94
- history.append({"role":"user", "content":message})
95
- history.append({"role":"assistant", "content":bot})
96
- return "", history
97
 
98
- if not category:
99
- bot = "Please select a category."
100
- history.append({"role":"user", "content":message})
101
- history.append({"role":"assistant", "content":bot})
102
- return "", history
103
 
104
- # Resolve sentiment
105
- if sentiment_choice == "auto":
106
- sent = infer_sentiment(message)
107
- else:
108
- sent = sentiment_choice # "positive" or "negative"
109
 
110
- bot = best_match_quote(category, sent, message)
111
- history.append({"role":"user", "content":message})
112
- history.append({"role":"assistant", "content":bot})
 
 
 
 
 
113
  return "", history
114
 
115
  def clear_chat():
116
  return None
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  def upload_json(filepath):
119
- global QUOTES, DATA_PATH
120
  try:
121
  with open(filepath, "r", encoding="utf-8") as f:
122
  data = json.load(f)
123
  if not isinstance(data, dict):
124
  return gr.update(value="Upload failed: JSON root must be an object."), gr.update(choices=[])
125
  QUOTES = data
126
- DATA_PATH = os.path.basename(filepath)
127
  cats = sorted(list(QUOTES.keys()))
128
- status = f"Loaded {len(cats)} categories from {DATA_PATH}."
129
- return status, gr.update(choices=cats, value=(cats[0] if cats else None))
130
  except Exception as e:
131
  return f"Error loading file: {e}", gr.update(choices=[])
132
 
133
- def download_current():
134
- out_name = DATA_PATH or "quotes_export.json"
135
- try:
136
- if DATA_PATH and os.path.exists(DATA_PATH):
137
- return DATA_PATH
138
- tmp = "quotes_export.json"
139
- with open(tmp, "w", encoding="utf-8") as f:
140
- json.dump(QUOTES, f, indent=2, ensure_ascii=False)
141
- return tmp
142
- except Exception:
143
- return None
144
-
145
- def export_chat(history):
146
- """Export conversation as CSV"""
147
- try:
148
- tmp = "conversation_export.csv"
149
- with open(tmp, "w", newline="", encoding="utf-8") as csvfile:
150
- writer = csv.writer(csvfile)
151
- writer.writerow(["role","message"])
152
- for msg in history:
153
- writer.writerow([msg["role"], msg["content"]])
154
- return tmp
155
- except Exception:
156
- return None
157
 
158
  # -----------------------------
159
- # UI
160
  # -----------------------------
161
  with gr.Blocks() as demo:
162
- gr.Markdown("## 🎓 College Life Chatbot — Category-Aware, Similarity Matching")
163
 
164
  initial_categories = sorted(list(QUOTES.keys()))
165
 
166
  with gr.Row():
167
- category = gr.Dropdown(label="Category",
168
- choices=initial_categories,
169
- value=(initial_categories[0] if initial_categories else None))
170
- sentiment = gr.Dropdown(label="Sentiment",
171
- choices=["auto","positive","negative"],
172
- value="auto",
173
- info="Auto infers from your question (good/bad/etc.)")
174
 
175
  chatbot = gr.Chatbot(label="Conversation", height=360, type="messages")
176
  msg = gr.Textbox(label="Your message", placeholder="Ask something like: 'Is food good in college?'", autofocus=True)
177
  send = gr.Button("Send")
178
  clear = gr.Button("Clear")
179
 
 
 
 
 
180
  with gr.Row():
181
  uploader = gr.File(label="Upload dataset (.json)", file_types=[".json"], type="filepath")
182
  upload_status = gr.Textbox(label="Upload status", interactive=False)
183
  downloader = gr.File(label="Download current dataset")
184
- export_btn = gr.File(label="Export conversation to CSV")
185
 
186
  # Wire events
187
  msg.submit(respond, [msg, chatbot, category, sentiment], [msg, chatbot])
188
  send.click(respond, [msg, chatbot, category, sentiment], [msg, chatbot])
189
  clear.click(clear_chat, None, chatbot, queue=False)
190
 
 
191
  uploader.upload(upload_json, uploader, [upload_status, category])
192
- downloader.download(download_current)
193
- export_btn.download(lambda history: export_chat(history), inputs=chatbot)
194
 
195
  # -----------------------------
196
  # Startup log
 
1
  import json
 
 
 
2
  import os
3
  import datetime
4
+ import random
5
+ import difflib
6
  import gradio as gr
7
+ import csv
8
 
9
  # -----------------------------
10
+ # Config / Data Loading
11
  # -----------------------------
12
  DATA_PATH = "quotes.json"
13
 
 
21
  return data
22
  except Exception as e:
23
  print(f"Failed to load {DATA_PATH}: {e}")
24
+ print("No dataset found. Upload one via the UI.")
25
  return {}
26
 
27
  QUOTES = load_quotes()
28
+ STAGED_RESPONSES = []
29
 
30
  # -----------------------------
31
+ # Utility Functions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  # -----------------------------
33
+ def best_match_quote(category, user_text):
34
+ if category not in QUOTES or not QUOTES[category]:
35
+ return "No quotes available for this category."
 
 
36
 
37
+ pool = QUOTES[category]
38
+ q_tokens = set(user_text.lower().split())
 
 
 
 
39
  best_quote = None
40
+ best_score = -1
41
+ for entry in pool:
42
+ quote = entry.get("quote", "")
43
+ qtoks = set(quote.lower().split())
44
  score = len(q_tokens & qtoks)
45
  if score > best_score:
46
  best_score = score
47
  best_quote = quote
48
 
49
+ if best_score == 0:
50
+ best_quote = random.choice(pool).get("quote", "")
51
  return best_quote
52
 
53
+ def generate_3fold_response(category, user_text):
54
+ # 1. Summary sentence (first line of best matching quote)
55
+ best_quote = best_match_quote(category, user_text)
56
+ summary = best_quote.split('.')[0] + '.'
 
 
 
 
 
57
 
58
+ # 2. Fusion of quotes in category
59
+ fused = " ".join([q.get("quote", "") for q in QUOTES.get(category, [])][:3])
60
+
61
+ # 3. Placeholder link (can be replaced with site search later)
62
+ link = f"https://www.google.com/search?q={category}+college+site:example.com"
63
 
64
+ return [
65
+ f"Summary: \n{summary}",
66
+ f"What real people say: \n{fused}",
67
+ f"Related link: \n{link}"
68
+ ]
69
 
70
+ # -----------------------------
71
+ # Gradio Callbacks
72
+ # -----------------------------
73
+ def respond(message, history, category, sentiment):
74
+ if not message.strip():
75
+ return "", history
76
+ responses = generate_3fold_response(category, message)
77
+ history.append((message, responses))
78
  return "", history
79
 
80
  def clear_chat():
81
  return None
82
 
83
+ def stage_response(chat_history, category):
84
+ if not chat_history:
85
+ return gr.update(value="No conversation to stage.")
86
+ last_user, last_bot = chat_history[-1]
87
+ if category not in QUOTES:
88
+ QUOTES[category] = []
89
+ QUOTES[category].append({"quote": last_bot[1].replace("What real people say: \n","")})
90
+ STAGED_RESPONSES.append((last_user, last_bot))
91
+ return gr.update(value=f"Last message staged to category '{category}'.")
92
+
93
+ def download_csv():
94
+ if not STAGED_RESPONSES:
95
+ return None
96
+ filename = "staged_conversation.csv"
97
+ with open(filename, "w", newline="", encoding="utf-8") as csvfile:
98
+ writer = csv.writer(csvfile)
99
+ writer.writerow(["User", "Summary", "What people say", "Link"])
100
+ for user_msg, bot_resp in STAGED_RESPONSES:
101
+ writer.writerow([user_msg] + bot_resp)
102
+ return filename
103
+
104
  def upload_json(filepath):
105
+ global QUOTES
106
  try:
107
  with open(filepath, "r", encoding="utf-8") as f:
108
  data = json.load(f)
109
  if not isinstance(data, dict):
110
  return gr.update(value="Upload failed: JSON root must be an object."), gr.update(choices=[])
111
  QUOTES = data
 
112
  cats = sorted(list(QUOTES.keys()))
113
+ return f"Loaded {len(cats)} categories from {filepath}.", gr.update(choices=cats, value=cats[0] if cats else None)
 
114
  except Exception as e:
115
  return f"Error loading file: {e}", gr.update(choices=[])
116
 
117
+ def download_dataset():
118
+ tmp = "quotes_export.json"
119
+ with open(tmp, "w", encoding="utf-8") as f:
120
+ json.dump(QUOTES, f, indent=2, ensure_ascii=False)
121
+ return tmp
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
  # -----------------------------
124
+ # Gradio UI
125
  # -----------------------------
126
  with gr.Blocks() as demo:
127
+ gr.Markdown("## 🎓 College Life Chatbot — 3-Fold Response + Staged Export")
128
 
129
  initial_categories = sorted(list(QUOTES.keys()))
130
 
131
  with gr.Row():
132
+ category = gr.Dropdown(label="Category", choices=initial_categories, value=(initial_categories[0] if initial_categories else None))
133
+ sentiment = gr.Dropdown(label="Sentiment", choices=["auto", "positive", "negative"], value="auto")
 
 
 
 
 
134
 
135
  chatbot = gr.Chatbot(label="Conversation", height=360, type="messages")
136
  msg = gr.Textbox(label="Your message", placeholder="Ask something like: 'Is food good in college?'", autofocus=True)
137
  send = gr.Button("Send")
138
  clear = gr.Button("Clear")
139
 
140
+ with gr.Row():
141
+ stage_btn = gr.Button("Stage conversation to category")
142
+ staged_status = gr.Textbox(label="Staging status", interactive=False)
143
+
144
  with gr.Row():
145
  uploader = gr.File(label="Upload dataset (.json)", file_types=[".json"], type="filepath")
146
  upload_status = gr.Textbox(label="Upload status", interactive=False)
147
  downloader = gr.File(label="Download current dataset")
148
+ download_csv_btn = gr.File(label="Download staged conversation CSV")
149
 
150
  # Wire events
151
  msg.submit(respond, [msg, chatbot, category, sentiment], [msg, chatbot])
152
  send.click(respond, [msg, chatbot, category, sentiment], [msg, chatbot])
153
  clear.click(clear_chat, None, chatbot, queue=False)
154
 
155
+ stage_btn.click(stage_response, [chatbot, category], staged_status)
156
  uploader.upload(upload_json, uploader, [upload_status, category])
157
+ downloader.download(download_dataset)
158
+ download_csv_btn.download(download_csv)
159
 
160
  # -----------------------------
161
  # Startup log