BasitAliii commited on
Commit
33718ef
Β·
verified Β·
1 Parent(s): 0645317

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +323 -19
app.py CHANGED
@@ -50,12 +50,14 @@ with st.sidebar:
50
  selected_user = st.selectbox(
51
  "Select profile",
52
  ["β€” Create New β€”"] + usernames,
 
53
  )
54
 
55
  st.markdown("### πŸ“ Profile Details")
56
  username = st.text_input(
57
  "Username",
58
  value="" if selected_user == "β€” Create New β€”" else selected_user,
 
59
  )
60
 
61
  offers_text = st.text_area(
@@ -63,6 +65,7 @@ with st.sidebar:
63
  height=90,
64
  value=st.session_state.get("offers_text", ""),
65
  placeholder="Python, React, Data Science",
 
66
  )
67
 
68
  wants_text = st.text_area(
@@ -70,23 +73,27 @@ with st.sidebar:
70
  height=90,
71
  value=st.session_state.get("wants_text", ""),
72
  placeholder="LLMs, DevOps, UI Design",
 
73
  )
74
 
75
  availability = st.text_input(
76
  "Availability",
77
  placeholder="Evenings / Weekends",
78
  value=st.session_state.get("availability", ""),
 
79
  )
80
 
81
  preferences = st.text_input(
82
  "Language / Preferences",
83
  placeholder="English, Hindi, Urdu",
84
  value=st.session_state.get("preferences", ""),
 
85
  )
86
 
87
  avatar_file = st.file_uploader(
88
  "Upload Avatar (Max 800MB)",
89
  type=["png", "jpg", "jpeg"],
 
90
  )
91
 
92
  # ---------- Avatar Preview ----------
@@ -101,7 +108,7 @@ with st.sidebar:
101
 
102
  # ---------- CREATE PROFILE ----------
103
  with col_create:
104
- if st.button("βž• Create", use_container_width=True):
105
  if not username.strip():
106
  st.error("Username is required!")
107
  elif store.find_by_username(username):
@@ -131,17 +138,20 @@ with st.sidebar:
131
  if ok:
132
  st.success(msg)
133
  # Clear all fields
134
- for key in ["username", "offers_text", "wants_text", "availability", "preferences"]:
135
- st.session_state[key] = ""
136
- # Clear selection
137
- st.session_state["selected_user"] = "β€” Create New β€”"
138
- st.experimental_rerun()
 
 
 
139
  else:
140
  st.error(msg)
141
 
142
  # ---------- UPDATE PROFILE ----------
143
  with col_update:
144
- if st.button("πŸ’Ύ Update", use_container_width=True):
145
  existing = store.find_by_username(username)
146
  if not existing:
147
  st.error("Profile does not exist. Use Create.")
@@ -163,19 +173,25 @@ with st.sidebar:
163
 
164
  store.add_or_update(existing)
165
  st.success("Profile updated")
166
- st.experimental_rerun()
 
 
 
167
 
168
  # ---------- DELETE PROFILE ----------
169
  if selected_user != "β€” Create New β€”":
170
- if st.button("πŸ—‘οΈ Delete Profile", type="secondary"):
171
  ok, msg = store.delete(selected_user)
172
  if ok:
173
  st.warning(msg)
174
  # Clear fields
175
- for key in ["username", "offers_text", "wants_text", "availability", "preferences"]:
176
- st.session_state[key] = ""
177
- st.session_state["selected_user"] = "β€” Create New β€”"
178
- st.experimental_rerun()
 
 
 
179
  else:
180
  st.error(msg)
181
 
@@ -220,9 +236,10 @@ with right:
220
  pick = st.selectbox(
221
  "Choose your profile",
222
  [p.username for p in profiles],
 
223
  )
224
 
225
- if st.button("✨ Find Best Matches"):
226
  with st.spinner("Analyzing profiles..."):
227
  current = store.find_by_username(pick)
228
 
@@ -249,17 +266,304 @@ with right:
249
  with st.sidebar:
250
  st.markdown("---")
251
  with st.expander("πŸ’¬ Feedback"):
252
- fb = st.text_area("Your feedback")
253
- if st.button("Submit Feedback"):
254
  if fb.strip():
255
  save_feedback(
256
  {
257
  "timestamp": time.time(),
258
  "feedback": fb,
259
- "username": st.session_state.get("username", "anonymous"),
260
  }
261
  )
262
  st.success("Thank you!")
263
- st.experimental_rerun()
 
 
 
264
  else:
265
- st.warning("Please write feedback.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  selected_user = st.selectbox(
51
  "Select profile",
52
  ["β€” Create New β€”"] + usernames,
53
+ key="selected_user",
54
  )
55
 
56
  st.markdown("### πŸ“ Profile Details")
57
  username = st.text_input(
58
  "Username",
59
  value="" if selected_user == "β€” Create New β€”" else selected_user,
60
+ key="username_input"
61
  )
62
 
63
  offers_text = st.text_area(
 
65
  height=90,
66
  value=st.session_state.get("offers_text", ""),
67
  placeholder="Python, React, Data Science",
68
+ key="offers_input"
69
  )
70
 
71
  wants_text = st.text_area(
 
73
  height=90,
74
  value=st.session_state.get("wants_text", ""),
75
  placeholder="LLMs, DevOps, UI Design",
76
+ key="wants_input"
77
  )
78
 
79
  availability = st.text_input(
80
  "Availability",
81
  placeholder="Evenings / Weekends",
82
  value=st.session_state.get("availability", ""),
83
+ key="availability_input"
84
  )
85
 
86
  preferences = st.text_input(
87
  "Language / Preferences",
88
  placeholder="English, Hindi, Urdu",
89
  value=st.session_state.get("preferences", ""),
90
+ key="preferences_input"
91
  )
92
 
93
  avatar_file = st.file_uploader(
94
  "Upload Avatar (Max 800MB)",
95
  type=["png", "jpg", "jpeg"],
96
+ key="avatar_uploader"
97
  )
98
 
99
  # ---------- Avatar Preview ----------
 
108
 
109
  # ---------- CREATE PROFILE ----------
110
  with col_create:
111
+ if st.button("βž• Create", use_container_width=True, key="create_button"):
112
  if not username.strip():
113
  st.error("Username is required!")
114
  elif store.find_by_username(username):
 
138
  if ok:
139
  st.success(msg)
140
  # Clear all fields
141
+ for key in ["offers_text", "wants_text", "availability", "preferences"]:
142
+ if key in st.session_state:
143
+ del st.session_state[key]
144
+ # Clear file uploader
145
+ if "avatar_uploader" in st.session_state:
146
+ del st.session_state["avatar_uploader"]
147
+
148
+ st.rerun()
149
  else:
150
  st.error(msg)
151
 
152
  # ---------- UPDATE PROFILE ----------
153
  with col_update:
154
+ if st.button("πŸ’Ύ Update", use_container_width=True, key="update_button"):
155
  existing = store.find_by_username(username)
156
  if not existing:
157
  st.error("Profile does not exist. Use Create.")
 
173
 
174
  store.add_or_update(existing)
175
  st.success("Profile updated")
176
+ # Clear file uploader after update
177
+ if "avatar_uploader" in st.session_state:
178
+ del st.session_state["avatar_uploader"]
179
+ st.rerun()
180
 
181
  # ---------- DELETE PROFILE ----------
182
  if selected_user != "β€” Create New β€”":
183
+ if st.button("πŸ—‘οΈ Delete Profile", type="secondary", key="delete_button"):
184
  ok, msg = store.delete(selected_user)
185
  if ok:
186
  st.warning(msg)
187
  # Clear fields
188
+ for key in ["offers_text", "wants_text", "availability", "preferences"]:
189
+ if key in st.session_state:
190
+ del st.session_state[key]
191
+ # Clear file uploader
192
+ if "avatar_uploader" in st.session_state:
193
+ del st.session_state["avatar_uploader"]
194
+ st.rerun()
195
  else:
196
  st.error(msg)
197
 
 
236
  pick = st.selectbox(
237
  "Choose your profile",
238
  [p.username for p in profiles],
239
+ key="matchmaking_select"
240
  )
241
 
242
+ if st.button("✨ Find Best Matches", key="find_matches_button"):
243
  with st.spinner("Analyzing profiles..."):
244
  current = store.find_by_username(pick)
245
 
 
266
  with st.sidebar:
267
  st.markdown("---")
268
  with st.expander("πŸ’¬ Feedback"):
269
+ fb = st.text_area("Your feedback", key="feedback_text")
270
+ if st.button("Submit Feedback", key="submit_feedback_button"):
271
  if fb.strip():
272
  save_feedback(
273
  {
274
  "timestamp": time.time(),
275
  "feedback": fb,
276
+ "username": username if username.strip() else "anonymous",
277
  }
278
  )
279
  st.success("Thank you!")
280
+ # Clear feedback text
281
+ if "feedback_text" in st.session_state:
282
+ del st.session_state["feedback_text"]
283
+ st.rerun()
284
  else:
285
+ st.warning("Please write feedback.")from __future__ import annotations
286
+
287
+ import time
288
+ import uuid
289
+ import re
290
+ from pathlib import Path
291
+ import streamlit as st
292
+
293
+ from config import AVATAR_DIR
294
+ from models import Profile
295
+ from storage import ProfileStore
296
+ from utils import normalize_skill_list, make_prompt_for_matching
297
+ from groq_client import ask_groq_for_matches
298
+ from matching import calculate_local_matches
299
+ from feedback import save_feedback
300
+ from ui_styles import BASE_CSS
301
+
302
+ # ---------- Streamlit Config ----------
303
+ st.set_page_config(
304
+ page_title="AI Skill Swap",
305
+ page_icon="🀝",
306
+ layout="wide",
307
+ )
308
+
309
+ st.markdown(f"<style>{BASE_CSS}</style>", unsafe_allow_html=True)
310
+
311
+ store = ProfileStore()
312
+
313
+ # ---------- Header ----------
314
+ col_logo, col_title = st.columns([1, 6])
315
+ with col_logo:
316
+ try:
317
+ st.image("logo.jpg", width=90) # Use uploaded logo
318
+ except:
319
+ st.image(
320
+ "https://via.placeholder.com/100x100/4F46E5/FFFFFF?text=🀝",
321
+ width=90,
322
+ )
323
+ with col_title:
324
+ st.title("AI Skill Swap")
325
+ st.caption("Teach what you know β€’ Learn what you love β€’ Match intelligently")
326
+
327
+ # ---------- Sidebar: Profile Management ----------
328
+ with st.sidebar:
329
+ st.header("πŸ‘€ Profile Manager")
330
+
331
+ profiles = store.load_all()
332
+ usernames = [p.username for p in profiles]
333
+
334
+ selected_user = st.selectbox(
335
+ "Select profile",
336
+ ["β€” Create New β€”"] + usernames,
337
+ key="selected_user",
338
+ )
339
+
340
+ st.markdown("### πŸ“ Profile Details")
341
+ username = st.text_input(
342
+ "Username",
343
+ value="" if selected_user == "β€” Create New β€”" else selected_user,
344
+ key="username_input"
345
+ )
346
+
347
+ offers_text = st.text_area(
348
+ "Skills you can teach",
349
+ height=90,
350
+ value=st.session_state.get("offers_text", ""),
351
+ placeholder="Python, React, Data Science",
352
+ key="offers_input"
353
+ )
354
+
355
+ wants_text = st.text_area(
356
+ "Skills you want to learn",
357
+ height=90,
358
+ value=st.session_state.get("wants_text", ""),
359
+ placeholder="LLMs, DevOps, UI Design",
360
+ key="wants_input"
361
+ )
362
+
363
+ availability = st.text_input(
364
+ "Availability",
365
+ placeholder="Evenings / Weekends",
366
+ value=st.session_state.get("availability", ""),
367
+ key="availability_input"
368
+ )
369
+
370
+ preferences = st.text_input(
371
+ "Language / Preferences",
372
+ placeholder="English, Hindi, Urdu",
373
+ value=st.session_state.get("preferences", ""),
374
+ key="preferences_input"
375
+ )
376
+
377
+ avatar_file = st.file_uploader(
378
+ "Upload Avatar (Max 800MB)",
379
+ type=["png", "jpg", "jpeg"],
380
+ key="avatar_uploader"
381
+ )
382
+
383
+ # ---------- Avatar Preview ----------
384
+ if avatar_file:
385
+ st.image(avatar_file, width=150, caption="Preview of avatar")
386
+ if avatar_file.size > 800 * 1024 * 1024:
387
+ st.error("File exceeds maximum size of 800MB!")
388
+ avatar_file = None
389
+
390
+ st.markdown("---")
391
+ col_create, col_update = st.columns(2)
392
+
393
+ # ---------- CREATE PROFILE ----------
394
+ with col_create:
395
+ if st.button("βž• Create", use_container_width=True, key="create_button"):
396
+ if not username.strip():
397
+ st.error("Username is required!")
398
+ elif store.find_by_username(username):
399
+ st.error("Profile already exists. Use Update.")
400
+ else:
401
+ avatar_path = None
402
+ if avatar_file:
403
+ safe = re.sub(r"[^A-Za-z0-9_.-]", "_", username)
404
+ ext = Path(avatar_file.name).suffix
405
+ avatar_path = str(
406
+ AVATAR_DIR / f"{safe}_{int(time.time())}{ext}"
407
+ )
408
+ with open(avatar_path, "wb") as f:
409
+ f.write(avatar_file.getbuffer())
410
+
411
+ profile = Profile(
412
+ id=str(uuid.uuid4()),
413
+ username=username.strip(),
414
+ offers=normalize_skill_list(offers_text),
415
+ wants=normalize_skill_list(wants_text),
416
+ availability=availability.strip(),
417
+ preferences=preferences.strip(),
418
+ avatar=avatar_path,
419
+ )
420
+
421
+ ok, msg = store.add_or_update(profile)
422
+ if ok:
423
+ st.success(msg)
424
+ # Clear all fields
425
+ for key in ["offers_text", "wants_text", "availability", "preferences"]:
426
+ if key in st.session_state:
427
+ del st.session_state[key]
428
+ # Clear file uploader
429
+ if "avatar_uploader" in st.session_state:
430
+ del st.session_state["avatar_uploader"]
431
+
432
+ st.rerun()
433
+ else:
434
+ st.error(msg)
435
+
436
+ # ---------- UPDATE PROFILE ----------
437
+ with col_update:
438
+ if st.button("πŸ’Ύ Update", use_container_width=True, key="update_button"):
439
+ existing = store.find_by_username(username)
440
+ if not existing:
441
+ st.error("Profile does not exist. Use Create.")
442
+ else:
443
+ existing.offers = normalize_skill_list(offers_text)
444
+ existing.wants = normalize_skill_list(wants_text)
445
+ existing.availability = availability.strip()
446
+ existing.preferences = preferences.strip()
447
+
448
+ if avatar_file:
449
+ safe = re.sub(r"[^A-Za-z0-9_.-]", "_", username)
450
+ ext = Path(avatar_file.name).suffix
451
+ avatar_path = str(
452
+ AVATAR_DIR / f"{safe}_{int(time.time())}{ext}"
453
+ )
454
+ with open(avatar_path, "wb") as f:
455
+ f.write(avatar_file.getbuffer())
456
+ existing.avatar = avatar_path
457
+
458
+ store.add_or_update(existing)
459
+ st.success("Profile updated")
460
+ # Clear file uploader after update
461
+ if "avatar_uploader" in st.session_state:
462
+ del st.session_state["avatar_uploader"]
463
+ st.rerun()
464
+
465
+ # ---------- DELETE PROFILE ----------
466
+ if selected_user != "β€” Create New β€”":
467
+ if st.button("πŸ—‘οΈ Delete Profile", type="secondary", key="delete_button"):
468
+ ok, msg = store.delete(selected_user)
469
+ if ok:
470
+ st.warning(msg)
471
+ # Clear fields
472
+ for key in ["offers_text", "wants_text", "availability", "preferences"]:
473
+ if key in st.session_state:
474
+ del st.session_state[key]
475
+ # Clear file uploader
476
+ if "avatar_uploader" in st.session_state:
477
+ del st.session_state["avatar_uploader"]
478
+ st.rerun()
479
+ else:
480
+ st.error(msg)
481
+
482
+ # ---------- MAIN CONTENT ----------
483
+ left, right = st.columns([2, 3])
484
+
485
+ # ---------- COMMUNITY PROFILES ----------
486
+ with left:
487
+ st.subheader("🌍 Community Profiles")
488
+ profiles = store.load_all()
489
+
490
+ if not profiles:
491
+ st.info("No profiles yet. Create one from the sidebar.")
492
+ else:
493
+ for p in profiles:
494
+ st.markdown("<div class='card'>", unsafe_allow_html=True)
495
+ cols = st.columns([1, 4])
496
+
497
+ with cols[0]:
498
+ if p.avatar and Path(p.avatar).exists():
499
+ st.image(p.avatar, width=120)
500
+ else:
501
+ st.image(
502
+ "https://via.placeholder.com/120",
503
+ width=120,
504
+ )
505
+
506
+ with cols[1]:
507
+ st.markdown(f"**{p.username}**")
508
+ st.caption(f"{p.availability} β€’ {p.preferences}")
509
+ st.markdown(f"🧠 **Offers:** {', '.join(p.offers) or 'β€”'}")
510
+ st.markdown(f"🎯 **Wants:** {', '.join(p.wants) or 'β€”'}")
511
+ st.markdown("</div>", unsafe_allow_html=True)
512
+
513
+ # ---------- AI MATCHMAKING ----------
514
+ with right:
515
+ st.subheader("πŸ€– AI Matchmaking")
516
+
517
+ if not profiles:
518
+ st.info("Add profiles to enable matchmaking.")
519
+ else:
520
+ pick = st.selectbox(
521
+ "Choose your profile",
522
+ [p.username for p in profiles],
523
+ key="matchmaking_select"
524
+ )
525
+
526
+ if st.button("✨ Find Best Matches", key="find_matches_button"):
527
+ with st.spinner("Analyzing profiles..."):
528
+ current = store.find_by_username(pick)
529
+
530
+ try:
531
+ sys_msg, user_msg = make_prompt_for_matching(current, profiles, 3)
532
+ matches = ask_groq_for_matches(sys_msg, user_msg)
533
+ except Exception:
534
+ matches = calculate_local_matches(
535
+ current,
536
+ [p for p in profiles if p.id != current.id],
537
+ 3,
538
+ )
539
+
540
+ for m in matches:
541
+ st.markdown("<div class='card'>", unsafe_allow_html=True)
542
+ st.markdown(f"### {m.get('username')}")
543
+ score = float(m.get("score", 0))
544
+ st.progress(score / 100)
545
+ st.caption(f"Match Score: {score}%")
546
+ st.caption(m.get("reason", "AI Match"))
547
+ st.markdown("</div>", unsafe_allow_html=True)
548
+
549
+ # ---------- FEEDBACK ----------
550
+ with st.sidebar:
551
+ st.markdown("---")
552
+ with st.expander("πŸ’¬ Feedback"):
553
+ fb = st.text_area("Your feedback", key="feedback_text")
554
+ if st.button("Submit Feedback", key="submit_feedback_button"):
555
+ if fb.strip():
556
+ save_feedback(
557
+ {
558
+ "timestamp": time.time(),
559
+ "feedback": fb,
560
+ "username": username if username.strip() else "anonymous",
561
+ }
562
+ )
563
+ st.success("Thank you!")
564
+ # Clear feedback text
565
+ if "feedback_text" in st.session_state:
566
+ del st.session_state["feedback_text"]
567
+ st.rerun()
568
+ else:
569
+ st.warning("Please write feedback.")