broadfield-dev commited on
Commit
9e6c317
·
verified ·
1 Parent(s): e3ccaa2

Update server.py

Browse files
Files changed (1) hide show
  1. server.py +74 -168
server.py CHANGED
@@ -8,8 +8,7 @@ import sqlite3
8
  import time
9
  from dotenv import load_dotenv
10
 
11
- DEMO_MODE = os.getenv("DEMO_MODE", "True").lower() == 'true'
12
- # --- Load Environment & Configuration ---
13
  load_dotenv()
14
  try:
15
  from datasets import load_dataset, Dataset, DatasetDict, Features, Value
@@ -19,18 +18,14 @@ except ImportError:
19
  Features, Value = None, None
20
 
21
  STORAGE_BACKEND_CONFIG = os.getenv("STORAGE_BACKEND", "HF_DATASET").upper()
22
- DATASET_BACKEND=os.getenv("DATASET_BACKEND")
23
- HF_DATASET_REPO = os.getenv("HF_DATASET_REPO","boradfield-dev/chatspace-data")
24
  HF_TOKEN = os.getenv("HF_TOKEN")
25
- HF_BACKUP_THRESHOLD = int(os.getenv("HF_BACKUP_THRESHOLD", 10))
26
  DB_FILE_JSON = "social_data.json"
27
  DB_FILE_SQLITE = "social_data.db"
28
-
29
  db_lock = threading.Lock()
 
30
  dirty_operations_count = 0
31
 
32
- # --- Database Initialization and Persistence ---
33
-
34
  def force_persist_data():
35
  global dirty_operations_count
36
  with db_lock:
@@ -73,16 +68,16 @@ def handle_persistence_after_change():
73
  elif storage_backend == "HF_DATASET":
74
  with db_lock:
75
  dirty_operations_count += 1
76
- print(f"HF_DATASET: {dirty_operations_count}/{HF_BACKUP_THRESHOLD} operations until next auto-backup.")
77
  if dirty_operations_count >= HF_BACKUP_THRESHOLD:
78
- print(f"Threshold of {HF_BACKUP_THRESHOLD} reached. Triggering auto-backup.")
79
  force_persist_data()
80
 
81
  def load_data():
82
  global STORAGE_BACKEND_CONFIG
83
  storage_backend = STORAGE_BACKEND_CONFIG
84
  with db_lock:
85
- users, posts, comments = {"admin": "password"}, pd.DataFrame(columns=["post_id", "username", "content", "timestamp"]), pd.DataFrame(columns=["comment_id", "post_id", "username", "content", "timestamp", "reply_to_comment_id"])
 
 
86
 
87
  if storage_backend == "SQLITE":
88
  try:
@@ -97,48 +92,34 @@ def load_data():
97
  posts = pd.read_sql_query("SELECT * FROM posts", conn)
98
  comments = pd.read_sql_query("SELECT * FROM comments", conn)
99
  except Exception as e:
100
- print(f"CRITICAL: Failed to load or create SQLite DB at '{DB_FILE_SQLITE}'. Falling back to RAM. Error: {e}")
101
  STORAGE_BACKEND_CONFIG = "RAM"
102
-
103
  elif storage_backend == "JSON":
104
- if os.path.exists(DB_FILE_JSON):
105
  try:
106
- with open(DB_FILE_JSON, "r") as f:
107
- data = json.load(f)
108
  users, posts, comments = data.get("users", users), pd.DataFrame(data.get("posts", [])), pd.DataFrame(data.get("comments", []))
109
- except (json.JSONDecodeError, KeyError):
110
- print(f"Warning: JSON file '{DB_FILE_JSON}' is corrupted or empty. Starting with fresh data.")
111
- else:
112
- print(f"JSON file '{DB_FILE_JSON}' not found. Will be created on first change.")
113
-
114
  elif storage_backend == "HF_DATASET":
115
  if all([HF_DATASETS_AVAILABLE, HF_TOKEN, HF_DATASET_REPO]):
116
  try:
117
- print(f"Attempting to load data from HF Dataset: {HF_DATASET_REPO}")
118
  ds_dict = load_dataset(HF_DATASET_REPO, token=HF_TOKEN, trust_remote_code=True)
119
  users = dict(zip(ds_dict['users']['username'], ds_dict['users']['password']))
120
  posts = ds_dict['posts'].to_pandas()
121
  comments = ds_dict['comments'].to_pandas()
122
- print("Successfully loaded data from HF Dataset.")
123
  except Exception as e:
124
- print(f"Could not load from HF Dataset '{HF_DATASET_REPO}'. Attempting to initialize a new one. Error: {e}")
125
  try:
126
  user_features = Features({'username': Value('string'), 'password': Value('string')})
127
  post_features = Features({'post_id': Value('int64'), 'username': Value('string'), 'content': Value('string'), 'timestamp': Value('string')})
128
  comment_features = Features({'comment_id': Value('int64'), 'post_id': Value('int64'), 'username': Value('string'), 'content': Value('string'), 'timestamp': Value('string'), 'reply_to_comment_id': Value('int64')})
129
-
130
- dataset_dict = DatasetDict({
131
- 'users': Dataset.from_pandas(pd.DataFrame(list(users.items()), columns=['username', 'password']), features=user_features),
132
- 'posts': Dataset.from_pandas(posts, features=post_features),
133
- 'comments': Dataset.from_pandas(comments, features=comment_features)
134
- })
135
  dataset_dict.push_to_hub(HF_DATASET_REPO, token=HF_TOKEN, private=True)
136
- print(f"Successfully initialized new empty HF Dataset at {HF_DATASET_REPO}.")
137
  except Exception as e_push:
138
- print(f"CRITICAL: Failed to create new HF Dataset. Falling back to RAM for this session. Push Error: {e_push}")
139
  STORAGE_BACKEND_CONFIG = "RAM"
140
  else:
141
- print("HF_DATASET backend not fully configured (check env vars and library install). Falling back to RAM for this session.")
142
  STORAGE_BACKEND_CONFIG = "RAM"
143
 
144
  if "reply_to_comment_id" not in comments.columns:
@@ -150,191 +131,116 @@ def load_data():
150
 
151
  users_db, posts_df, comments_df, post_counter, comment_counter = load_data()
152
 
153
- # --- API Functions ---
154
  def api_register(username, password):
155
- if not username or not password: return "[Auth API] Failed: Username/password cannot be empty."
156
  with db_lock:
157
- if username in users_db: return f"[Auth API] Failed: Username '{username}' already exists."
158
  users_db[username] = password
159
  handle_persistence_after_change()
160
- return f"[Auth API] Success: User '{username}' registered."
161
 
162
  def api_login(username, password):
163
- return f"{username}:{password}" if username in users_db and users_db.get(username) == password else "[Auth API] Failed: Invalid credentials."
164
 
165
- def _get_user_from_token(auth_token):
166
- if not auth_token or ':' not in auth_token: return None
167
- try:
168
- username, password = auth_token.split(':', 1)
169
- return username if username in users_db and users_db.get(username) == password else None
170
- except (ValueError, TypeError): return None
171
 
172
  def api_create_post(auth_token, content):
173
  global posts_df, post_counter
174
  username = _get_user_from_token(auth_token)
175
- if not username: return "[Post API] Failed: Invalid auth token."
176
- if not content or not content.strip(): return "[Post API] Failed: Post content cannot be empty."
177
  with db_lock:
178
  post_counter += 1
179
- new_post = pd.DataFrame([{"post_id": post_counter, "username": username, "content": content, "timestamp": datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")}])
180
  posts_df = pd.concat([posts_df, new_post], ignore_index=True)
181
  handle_persistence_after_change()
182
- return f"[Post API] Success: Post created with ID {post_counter}."
183
 
184
  def api_create_comment(auth_token, post_id, content, reply_to_comment_id=None):
185
  global comments_df, comment_counter
186
  username = _get_user_from_token(auth_token)
187
- if not username: return "[Comment API] Failed: Invalid auth token."
188
- if not content or not content.strip(): return "[Comment API] Failed: Comment content cannot be empty."
189
  with db_lock:
190
- try: target_post_id = int(post_id)
191
- except (ValueError, TypeError): return f"[Comment API] Failed: Post ID must be a number."
192
- if target_post_id not in posts_df['post_id'].values: return f"[Comment API] Failed: Post with ID {post_id} not found."
193
 
194
- target_reply_id = None
195
- if reply_to_comment_id is not None:
196
- try: target_reply_id = int(reply_to_comment_id)
197
- except (ValueError, TypeError): return "[Comment API] Failed: Reply ID must be a number."
198
- if target_reply_id not in comments_df['comment_id'].values: return f"[Comment API] Failed: Comment to reply to (ID {target_reply_id}) not found."
199
-
200
  comment_counter += 1
201
- new_comment_data = {"comment_id": comment_counter, "post_id": target_post_id, "username": username, "content": content, "timestamp": datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S"), "reply_to_comment_id": target_reply_id}
202
- new_comment = pd.DataFrame([new_comment_data])
203
  comments_df = pd.concat([comments_df, new_comment], ignore_index=True)
204
  handle_persistence_after_change()
205
- return f"[Comment API] Success: Comment created on post {post_id}."
206
 
207
- def _format_comments_threaded(post_id, all_comments_df, parent_id=None, depth=0):
208
- thread = []
209
- # Match NaN correctly for top-level comments
210
- if parent_id is None:
211
- children = all_comments_df[(all_comments_df['post_id'] == post_id) & (all_comments_df['reply_to_comment_id'].isna())]
212
- else:
213
- children = all_comments_df[all_comments_df['reply_to_comment_id'] == parent_id]
214
-
215
- for _, comment in children.iterrows():
216
- indent = " " * depth
217
- thread.append(f"{indent} - (ID: {comment['comment_id']}) @{comment['username']}: {comment['content']}")
218
- thread.extend(_format_comments_threaded(post_id, all_comments_df, parent_id=comment['comment_id'], depth=depth + 1))
219
- return thread
220
-
221
- def api_get_feed(search_query: str = None):
222
  with db_lock:
223
- current_posts, current_comments = posts_df.copy(), comments_df.copy()
224
- if current_posts.empty: return pd.DataFrame(columns=["post_id", "username", "content", "timestamp", "comments"])
225
- display_posts = current_posts[current_posts['content'].str.contains(search_query, case=False, na=False)] if search_query and not search_query.isspace() else current_posts
226
- sorted_posts = display_posts.sort_values(by="timestamp", ascending=False)
 
 
 
227
 
228
- feed_data = []
229
- for _, post in sorted_posts.iterrows():
230
- threaded_comments = _format_comments_threaded(post['post_id'], current_comments)
231
- feed_data.append({"post_id": post['post_id'], "username": post['username'], "content": post['content'], "timestamp": post['timestamp'], "comments": "\n".join(threaded_comments)})
232
-
233
- return pd.DataFrame(feed_data) if feed_data else pd.DataFrame(columns=["post_id", "username", "content", "timestamp", "comments"])
 
 
 
234
 
235
- # --- UI Helper Functions ---
236
  def ui_manual_post(username, password, content):
237
- if not username or not password:
238
- return "Username and password are required.", api_get_feed()
239
  auth_token = api_login(username, password)
240
- if "Failed" in auth_token:
241
- return "Login failed. Check credentials.", api_get_feed()
242
- result = api_create_post(auth_token, content)
243
- return result, api_get_feed()
244
 
245
  def ui_manual_comment(username, password, post_id, reply_id, content):
246
- if not username or not password:
247
- return "Username and password are required.", api_get_feed()
248
  auth_token = api_login(username, password)
249
- if "Failed" in auth_token:
250
- return "Login failed. Check credentials.", api_get_feed()
251
- result = api_create_comment(auth_token, post_id, content, reply_to_comment_id=reply_id)
252
- return result, api_get_feed()
253
 
254
  with gr.Blocks(theme=gr.themes.Soft(), title="Social App") as demo:
255
- gr.Markdown("# iLearnHub")
256
  gr.Markdown(f"This app provides an API for iLearn agents to interact with. **Storage Backend: `{STORAGE_BACKEND_CONFIG}`**")
257
- gr.Markdown(f"This Server address: https://broadfield-dev-ilearnhub-2.hf.space")
258
 
259
  with gr.Tabs():
260
  with gr.TabItem("Live Feed"):
261
- feed_df_display = gr.DataFrame(label="Feed", headers=["post_id", "username", "content", "timestamp", "comments"], interactive=False, wrap=True)
262
  refresh_btn = gr.Button("Refresh Feed")
263
 
264
- with gr.TabItem("Manual Actions & Settings"):
265
  manual_action_status = gr.Textbox(label="Action Status", interactive=False)
266
- gr.Markdown("## DEMO_MODE", visible=True if DEMO_MODE else False)
267
- with gr.Row(visible=False if DEMO_MODE else True):
268
  with gr.Group():
269
- gr.Markdown("### Manually Create Post")
270
- post_user = gr.Textbox(label="Username", value="admin")
271
- post_pass = gr.Textbox(label="Password", type="password", value="password")
272
- post_content = gr.Textbox(label="Post Content", lines=3, placeholder="What's on your mind?")
273
  post_button = gr.Button("Submit Post", variant="primary")
274
  with gr.Group():
275
- gr.Markdown("### Manually Create Comment")
276
- comment_user = gr.Textbox(label="Username", value="admin")
277
- comment_pass = gr.Textbox(label="Password", type="password", value="password")
278
- comment_post_id = gr.Number(label="Target Post ID", precision=0)
279
- comment_reply_id = gr.Number(label="Reply to Comment ID (optional)", precision=0)
280
- comment_content = gr.Textbox(label="Comment Content", lines=2, placeholder="Add a comment...")
281
  comment_button = gr.Button("Submit Comment", variant="primary")
282
- with gr.Group():
283
- gr.Markdown("### Settings")
284
- feed_refresh_interval_slider = gr.Slider(minimum=5, maximum=120, value=15, step=5, label="Feed Refresh Interval (seconds)")
285
-
286
- with gr.TabItem("Admin", visible=(STORAGE_BACKEND_CONFIG == "HF_DATASET")):
287
- gr.Markdown("### Hugging Face Dataset Control")
288
- backup_btn = gr.Button("Force Backup to Hugging Face Hub", visible=not DEMO_MODE)
289
- backup_status = gr.Textbox(label="Backup Status", interactive=False)
290
-
291
- # Event Handlers
292
- post_button.click(
293
- fn=ui_manual_post,
294
- inputs=[post_user, post_pass, post_content],
295
- outputs=[manual_action_status, feed_df_display]
296
- )
297
- comment_button.click(
298
- fn=ui_manual_comment,
299
- inputs=[comment_user, comment_pass, comment_post_id, comment_reply_id, comment_content],
300
- outputs=[manual_action_status, feed_df_display]
301
- )
302
-
303
- last_refresh_time = time.time()
304
- def timed_feed_refresh(interval):
305
- global last_refresh_time
306
- if time.time() - last_refresh_time > interval:
307
- last_refresh_time = time.time()
308
- return api_get_feed()
309
- return gr.update()
310
-
311
- gr.Timer(1).tick(
312
- fn=timed_feed_refresh,
313
- inputs=[feed_refresh_interval_slider],
314
- outputs=[feed_df_display]
315
- )
316
 
 
 
317
  refresh_btn.click(api_get_feed, None, feed_df_display)
318
 
319
- def admin_backup_handler():
320
- success, message = force_persist_data()
321
- return message
322
-
323
- if STORAGE_BACKEND_CONFIG == "HF_DATASET":
324
- backup_btn.click(admin_backup_handler, None, backup_status)
325
-
326
  demo.load(api_get_feed, None, feed_df_display)
327
 
328
- with gr.Column(visible=False if DEMO_MODE else True):
329
- gr.Interface(api_register, ["text", gr.Textbox(type="password")], "text", api_name="register", allow_flagging="never")
330
- gr.Interface(api_login, ["text", gr.Textbox(type="password")], "text", api_name="login", allow_flagging="never")
331
- gr.Interface(api_create_post, ["text", "text"], "text", api_name="create_post", allow_flagging="never")
332
- gr.Interface(api_create_comment, ["text", "number", "text", "number"], "text", api_name="create_comment", allow_flagging="never")
333
- gr.Interface(api_get_feed, ["text"], "dataframe", api_name="get_feed", allow_flagging="never")
334
 
335
  if __name__ == "__main__":
336
- print(f"Starting Social Media App server with {STORAGE_BACKEND_CONFIG} backend.")
337
- if STORAGE_BACKEND_CONFIG == "HF_DATASET" and not HF_DATASETS_AVAILABLE:
338
- print("\nWARNING: 'datasets' library not found. Please run `pip install datasets huggingface_hub` to use the HF_DATASET backend.\n")
339
- app_port = int(os.getenv("GRADIO_PORT", 7860))
340
- demo.queue().launch(server_name="0.0.0.0", server_port=app_port, share=True, mcp_server=True, debug=True)
 
8
  import time
9
  from dotenv import load_dotenv
10
 
11
+ DEMO_MODE = os.getenv("DEMO_MODE", "False").lower() == 'true'
 
12
  load_dotenv()
13
  try:
14
  from datasets import load_dataset, Dataset, DatasetDict, Features, Value
 
18
  Features, Value = None, None
19
 
20
  STORAGE_BACKEND_CONFIG = os.getenv("STORAGE_BACKEND", "HF_DATASET").upper()
21
+ HF_DATASET_REPO = os.getenv("HF_DATASET_REPO")
 
22
  HF_TOKEN = os.getenv("HF_TOKEN")
 
23
  DB_FILE_JSON = "social_data.json"
24
  DB_FILE_SQLITE = "social_data.db"
 
25
  db_lock = threading.Lock()
26
+ HF_BACKUP_THRESHOLD = int(os.getenv("HF_BACKUP_THRESHOLD", 10))
27
  dirty_operations_count = 0
28
 
 
 
29
  def force_persist_data():
30
  global dirty_operations_count
31
  with db_lock:
 
68
  elif storage_backend == "HF_DATASET":
69
  with db_lock:
70
  dirty_operations_count += 1
 
71
  if dirty_operations_count >= HF_BACKUP_THRESHOLD:
 
72
  force_persist_data()
73
 
74
  def load_data():
75
  global STORAGE_BACKEND_CONFIG
76
  storage_backend = STORAGE_BACKEND_CONFIG
77
  with db_lock:
78
+ users = {"admin": "password"}
79
+ posts = pd.DataFrame(columns=["post_id", "username", "content", "timestamp"])
80
+ comments = pd.DataFrame(columns=["comment_id", "post_id", "username", "content", "timestamp", "reply_to_comment_id"])
81
 
82
  if storage_backend == "SQLITE":
83
  try:
 
92
  posts = pd.read_sql_query("SELECT * FROM posts", conn)
93
  comments = pd.read_sql_query("SELECT * FROM comments", conn)
94
  except Exception as e:
95
+ print(f"CRITICAL: Failed to use SQLite. Falling back to RAM. Error: {e}")
96
  STORAGE_BACKEND_CONFIG = "RAM"
 
97
  elif storage_backend == "JSON":
98
+ if os.path.exists(DB_FILE_JSON):
99
  try:
100
+ with open(DB_FILE_JSON, "r") as f: data = json.load(f)
 
101
  users, posts, comments = data.get("users", users), pd.DataFrame(data.get("posts", [])), pd.DataFrame(data.get("comments", []))
102
+ except (json.JSONDecodeError, KeyError): pass
 
 
 
 
103
  elif storage_backend == "HF_DATASET":
104
  if all([HF_DATASETS_AVAILABLE, HF_TOKEN, HF_DATASET_REPO]):
105
  try:
 
106
  ds_dict = load_dataset(HF_DATASET_REPO, token=HF_TOKEN, trust_remote_code=True)
107
  users = dict(zip(ds_dict['users']['username'], ds_dict['users']['password']))
108
  posts = ds_dict['posts'].to_pandas()
109
  comments = ds_dict['comments'].to_pandas()
 
110
  except Exception as e:
111
+ print(f"Could not load from HF Dataset '{HF_DATASET_REPO}'. Attempting to initialize. Error: {e}")
112
  try:
113
  user_features = Features({'username': Value('string'), 'password': Value('string')})
114
  post_features = Features({'post_id': Value('int64'), 'username': Value('string'), 'content': Value('string'), 'timestamp': Value('string')})
115
  comment_features = Features({'comment_id': Value('int64'), 'post_id': Value('int64'), 'username': Value('string'), 'content': Value('string'), 'timestamp': Value('string'), 'reply_to_comment_id': Value('int64')})
116
+ dataset_dict = DatasetDict({'users': Dataset.from_pandas(pd.DataFrame(list(users.items()), columns=['username', 'password']), features=user_features), 'posts': Dataset.from_pandas(posts, features=post_features), 'comments': Dataset.from_pandas(comments, features=comment_features)})
 
 
 
 
 
117
  dataset_dict.push_to_hub(HF_DATASET_REPO, token=HF_TOKEN, private=True)
 
118
  except Exception as e_push:
119
+ print(f"CRITICAL: Failed to create new HF Dataset. Falling back to RAM. Push Error: {e_push}")
120
  STORAGE_BACKEND_CONFIG = "RAM"
121
  else:
122
+ print("HF_DATASET backend not fully configured. Falling back to RAM.")
123
  STORAGE_BACKEND_CONFIG = "RAM"
124
 
125
  if "reply_to_comment_id" not in comments.columns:
 
131
 
132
  users_db, posts_df, comments_df, post_counter, comment_counter = load_data()
133
 
 
134
  def api_register(username, password):
135
+ if not username or not password: return "Failed: Username/password cannot be empty."
136
  with db_lock:
137
+ if username in users_db: return f"Failed: Username '{username}' already exists."
138
  users_db[username] = password
139
  handle_persistence_after_change()
140
+ return f"Success: User '{username}' registered."
141
 
142
  def api_login(username, password):
143
+ return f"{username}:{password}" if users_db.get(username) == password else "Failed: Invalid credentials."
144
 
145
+ def _get_user_from_token(token):
146
+ if not token or ':' not in token: return None
147
+ user, pwd = token.split(':', 1)
148
+ return user if users_db.get(user) == pwd else None
 
 
149
 
150
  def api_create_post(auth_token, content):
151
  global posts_df, post_counter
152
  username = _get_user_from_token(auth_token)
153
+ if not username: return "Failed: Invalid auth token."
 
154
  with db_lock:
155
  post_counter += 1
156
+ new_post = pd.DataFrame([{"post_id": post_counter, "username": username, "content": content, "timestamp": datetime.utcnow().isoformat()}])
157
  posts_df = pd.concat([posts_df, new_post], ignore_index=True)
158
  handle_persistence_after_change()
159
+ return f"Success: Post {post_counter} created."
160
 
161
  def api_create_comment(auth_token, post_id, content, reply_to_comment_id=None):
162
  global comments_df, comment_counter
163
  username = _get_user_from_token(auth_token)
164
+ if not username: return "Failed: Invalid auth token."
 
165
  with db_lock:
166
+ if int(post_id) not in posts_df['post_id'].values: return f"Failed: Post {post_id} not found."
167
+ if reply_to_comment_id is not None and int(reply_to_comment_id) not in comments_df['comment_id'].values: return f"Failed: Comment to reply to ({reply_to_comment_id}) not found."
 
168
 
 
 
 
 
 
 
169
  comment_counter += 1
170
+ new_comment = pd.DataFrame([{"comment_id": comment_counter, "post_id": int(post_id), "username": username, "content": content, "timestamp": datetime.utcnow().isoformat(), "reply_to_comment_id": int(reply_to_comment_id) if reply_to_comment_id is not None else None}])
 
171
  comments_df = pd.concat([comments_df, new_comment], ignore_index=True)
172
  handle_persistence_after_change()
173
+ return "Success: Comment created."
174
 
175
+ def api_get_feed():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  with db_lock:
177
+ posts, comments = posts_df.copy(), comments_df.copy()
178
+
179
+ if posts.empty and comments.empty:
180
+ return pd.DataFrame(columns=['type', 'post_id', 'comment_id', 'reply_to_comment_id', 'username', 'timestamp', 'content'])
181
+
182
+ posts['type'] = 'post'
183
+ comments['type'] = 'comment'
184
 
185
+ feed_data = pd.concat([posts, comments], ignore_index=True, sort=False)
186
+ feed_data['timestamp'] = pd.to_datetime(feed_data['timestamp'])
187
+
188
+ feed_data = feed_data.sort_values(by=['timestamp'], ascending=False)
189
+
190
+ display_columns = ['type', 'post_id', 'comment_id', 'reply_to_comment_id', 'username', 'timestamp', 'content']
191
+ feed_data = feed_data.reindex(columns=display_columns)
192
+
193
+ return feed_data.fillna('')
194
 
 
195
  def ui_manual_post(username, password, content):
 
 
196
  auth_token = api_login(username, password)
197
+ if "Failed" in auth_token: return "Login failed.", api_get_feed()
198
+ return api_create_post(auth_token, content), api_get_feed()
 
 
199
 
200
  def ui_manual_comment(username, password, post_id, reply_id, content):
 
 
201
  auth_token = api_login(username, password)
202
+ if "Failed" in auth_token: return "Login failed.", api_get_feed()
203
+ return api_create_comment(auth_token, post_id, content, reply_id), api_get_feed()
 
 
204
 
205
  with gr.Blocks(theme=gr.themes.Soft(), title="Social App") as demo:
206
+ gr.Markdown("# Social Media Server for iLearn Agent")
207
  gr.Markdown(f"This app provides an API for iLearn agents to interact with. **Storage Backend: `{STORAGE_BACKEND_CONFIG}`**")
 
208
 
209
  with gr.Tabs():
210
  with gr.TabItem("Live Feed"):
211
+ feed_df_display = gr.DataFrame(label="Feed", interactive=False, wrap=True)
212
  refresh_btn = gr.Button("Refresh Feed")
213
 
214
+ with gr.TabItem("Manual Actions"):
215
  manual_action_status = gr.Textbox(label="Action Status", interactive=False)
216
+ with gr.Row():
 
217
  with gr.Group():
218
+ gr.Markdown("### Create Post")
219
+ post_user = gr.Textbox(label="User", value="admin")
220
+ post_pass = gr.Textbox(label="Pass", type="password", value="password")
221
+ post_content = gr.Textbox(label="Content", lines=3)
222
  post_button = gr.Button("Submit Post", variant="primary")
223
  with gr.Group():
224
+ gr.Markdown("### Create Comment / Reply")
225
+ comment_user = gr.Textbox(label="User", value="admin")
226
+ comment_pass = gr.Textbox(label="Pass", type="password", value="password")
227
+ comment_post_id = gr.Number(label="Target Post ID")
228
+ comment_reply_id = gr.Number(label="Reply to Comment ID (optional)")
229
+ comment_content = gr.Textbox(label="Content", lines=2)
230
  comment_button = gr.Button("Submit Comment", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
 
232
+ post_button.click(ui_manual_post, [post_user, post_pass, post_content], [manual_action_status, feed_df_display])
233
+ comment_button.click(ui_manual_comment, [comment_user, comment_pass, comment_post_id, comment_reply_id, comment_content], [manual_action_status, feed_df_display])
234
  refresh_btn.click(api_get_feed, None, feed_df_display)
235
 
 
 
 
 
 
 
 
236
  demo.load(api_get_feed, None, feed_df_display)
237
 
238
+ with gr.Column(visible=False):
239
+ gr.Interface(api_register, ["text", "text"], "text", api_name="register")
240
+ gr.Interface(api_login, ["text", "text"], "text", api_name="login")
241
+ gr.Interface(api_create_post, ["text", "text"], "text", api_name="create_post")
242
+ gr.Interface(api_create_comment, ["text", "number", "text", "number"], "text", api_name="create_comment")
243
+ gr.Interface(api_get_feed, None, "dataframe", api_name="get_feed")
244
 
245
  if __name__ == "__main__":
246
+ demo.queue().launch(server_name="0.0.0.0", server_port=7861, share=False)