BasitAliii commited on
Commit
dc01735
Β·
verified Β·
1 Parent(s): f64656d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +178 -224
app.py CHANGED
@@ -15,7 +15,9 @@ from matching import calculate_local_matches
15
  from feedback import save_feedback
16
  from ui_styles import BASE_CSS
17
 
18
- # ---------- Streamlit Config ----------
 
 
19
  st.set_page_config(
20
  page_title="AI Skill Swap",
21
  page_icon="🀝",
@@ -24,283 +26,235 @@ st.set_page_config(
24
 
25
  st.markdown(f"<style>{BASE_CSS}</style>", unsafe_allow_html=True)
26
 
27
- # ---------- Header ----------
28
- col_logo, col_title = st.columns([1, 8])
 
 
29
 
30
- with col_logo:
31
- try:
32
- st.image("logo.jpg", width=100)
33
- except:
34
- st.image(
35
- "https://via.placeholder.com/100x100/4F46E5/FFFFFF?text=🀝",
36
- width=100,
37
- )
38
 
39
- with col_title:
40
- st.title("AI Skill Swap β€” Match & Exchange Skills")
41
- st.caption("Connect, Learn, and Grow Together")
42
 
43
- # ---------- Store ----------
44
  store = ProfileStore()
45
 
46
- # ---------- Sidebar: Profile Form ----------
 
 
47
  with st.sidebar:
48
- st.header("Your profile")
49
-
50
- with st.form("profile_form"):
51
- username = st.text_input(
52
- "Username",
53
- value=st.session_state.get("username", ""),
54
- )
55
-
56
- offers_text = st.text_area(
57
- "Skills you can teach",
58
- value=st.session_state.get("offers_text", ""),
59
- )
60
-
61
- wants_text = st.text_area(
62
- "Skills you want to learn",
63
- value=st.session_state.get("wants_text", ""),
64
- )
65
-
66
- availability = st.text_input(
67
- "Availability (Day/Night)",
68
- value=st.session_state.get("availability", ""),
69
- )
70
-
71
- preferences = st.text_input(
72
- "Language Preferences",
73
- value=st.session_state.get("preferences", ""),
74
- )
75
 
76
- avatar_file = st.file_uploader(
77
- "Upload avatar (optional)",
78
- type=["png", "jpg", "jpeg"],
79
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
- save = st.form_submit_button("Save / Update profile")
82
 
83
- if save:
84
- offers = normalize_skill_list(offers_text)
85
- wants = normalize_skill_list(wants_text)
86
 
87
- avatar_path = None
88
- if avatar_file and username.strip():
89
- safe = re.sub(r"[^A-Za-z0-9_.-]", "_", username.strip())
90
- ext = Path(avatar_file.name).suffix
91
- avatar_path = str(
92
- AVATAR_DIR / f"{safe}_{int(time.time())}{ext}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  )
94
- with open(avatar_path, "wb") as f:
95
- f.write(avatar_file.getbuffer())
96
-
97
- profile = Profile(
98
- id=str(uuid.uuid4()),
99
- username=username.strip(),
100
- offers=offers,
101
- wants=wants,
102
- availability=availability.strip(),
103
- preferences=preferences.strip(),
104
- avatar=avatar_path,
105
- )
106
-
107
- ok, msg = store.add_or_update(profile)
108
-
109
- if ok:
110
  st.success(msg)
111
- st.session_state["username"] = username
112
- st.session_state["offers_text"] = offers_text
113
- st.session_state["wants_text"] = wants_text
114
- st.session_state["availability"] = availability
115
- st.session_state["preferences"] = preferences
 
 
 
116
  else:
117
- st.error(msg)
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
- st.markdown("---")
120
- st.header("Load / Delete")
 
121
 
122
- profiles = store.load_all()
123
- options = ["-- new profile --"] + [p.username for p in profiles]
124
- selected = st.selectbox("Choose profile", options)
125
-
126
- if st.button("Load profile") and selected != "-- new profile --":
127
- p = store.find_by_username(selected)
128
- if p:
129
- st.session_state["username"] = p.username
130
- st.session_state["offers_text"] = "\n".join(p.offers)
131
- st.session_state["wants_text"] = "\n".join(p.wants)
132
- st.session_state["availability"] = p.availability
133
- st.session_state["preferences"] = p.preferences
134
  st.rerun()
135
 
136
- if st.button("Delete profile") and selected != "-- new profile --":
137
- ok, msg = store.delete(selected)
138
- if ok:
139
- st.success(msg)
140
- for k in [
141
- "username",
142
- "offers_text",
143
- "wants_text",
144
- "availability",
145
- "preferences",
146
- ]:
147
- st.session_state.pop(k, None)
148
- time.sleep(0.2)
149
- st.rerun()
150
 
151
- # ---------- Main Layout ----------
152
- col1, col2 = st.columns([2, 3])
 
 
 
153
 
154
- # ---------- Community Profiles ----------
155
- with col1:
156
- st.subheader("Community profiles")
157
  profiles = store.load_all()
158
 
159
  if not profiles:
160
- st.info("No profiles yet.")
161
  else:
162
  for p in profiles:
163
- c1, c2 = st.columns([1, 4])
 
164
 
165
- with c1:
166
  if p.avatar and Path(p.avatar).exists():
167
- st.image(p.avatar, width=64)
168
  else:
169
- st.image(
170
- "https://via.placeholder.com/64?text=Avatar",
171
- width=64,
172
- )
173
 
174
- with c2:
175
  st.markdown(f"**{p.username}**")
176
- st.markdown(f"Offers: {', '.join(p.offers) or 'β€”'}")
177
- st.markdown(f"Wants: {', '.join(p.wants) or 'β€”'}")
178
  st.caption(f"{p.availability} β€’ {p.preferences}")
 
 
179
 
180
- # ---------- AI Matchmaking ----------
181
- with col2:
182
- st.subheader("Find Matches (AI)")
183
- profiles = store.load_all()
 
 
 
184
 
185
  if not profiles:
186
- st.info("Add profiles first.")
187
  else:
188
  pick = st.selectbox(
189
- "Match for profile",
190
  [p.username for p in profiles],
191
  )
192
 
193
- top_k = 3
194
-
195
- if st.button("Run AI matchmaking"):
196
- with st.spinner("Finding matches..."):
197
  current = store.find_by_username(pick)
198
 
199
- if not current:
200
- st.error("Selected profile not found!")
201
- st.stop()
202
-
203
  try:
204
- sys_ins, user_msg = make_prompt_for_matching(
205
- current,
206
- profiles,
207
- top_k,
208
  )
209
- matches = ask_groq_for_matches(
210
- sys_ins,
211
- user_msg,
212
- )
213
-
214
- for m in matches:
215
- if "score" not in m:
216
- m["score"] = 0
217
-
218
- except Exception as e:
219
- st.warning(
220
- f"AI matching failed: {e}. Using local matching..."
221
- )
222
- candidates = [
223
- p for p in profiles if p.id != current.id
224
- ]
225
  matches = calculate_local_matches(
226
  current,
227
- candidates,
228
- top_k,
229
  )
230
 
231
- st.markdown(
232
- "<div class='card'><b>Top Matches</b></div>",
233
- unsafe_allow_html=True,
234
- )
235
-
236
  for m in matches:
237
- c1, c2 = st.columns([1, 5])
238
-
239
- with c1:
240
- avatar = m.get("avatar")
241
- if avatar and Path(avatar).exists():
242
- st.image(avatar, width=72)
243
- else:
244
- st.image(
245
- "https://via.placeholder.com/72?text=User",
246
- width=72,
247
- )
248
-
249
- with c2:
250
- st.markdown(
251
- f"### {m.get('username', 'Unknown User')}"
252
- )
253
-
254
- score = m.get("score", 0)
255
- try:
256
- score = float(score)
257
- except:
258
- score = 0
259
-
260
- score = max(0, min(100, score))
261
-
262
- st.markdown(
263
- f"**Match score:** "
264
- f"<span class='match-score'>{score}%</span>",
265
- unsafe_allow_html=True,
266
- )
267
-
268
- st.progress(score / 100)
269
- st.caption(
270
- f"*{m.get('reason', 'AI matchmaking')}*"
271
- )
272
-
273
- # ---------- Floating Feedback Button ----------
274
- st.markdown(
275
- """
276
- <div class='feedback-floating'
277
- onclick="document.querySelector('.feedback-form').style.display='block'">
278
- πŸ’¬
279
- </div>
280
- """,
281
- unsafe_allow_html=True,
282
- )
283
-
284
- # ---------- Feedback ----------
285
  with st.sidebar:
286
  st.markdown("---")
287
  with st.expander("πŸ’¬ Feedback"):
288
- feedback = st.text_area("Your feedback")
289
-
290
- if st.button("Submit Feedback"):
291
- if feedback.strip():
292
  save_feedback(
293
  {
294
  "timestamp": time.time(),
295
- "feedback": feedback.strip(),
296
- "username": st.session_state.get(
297
- "username",
298
- "anonymous",
299
- ),
300
  }
301
  )
302
- st.success("Thank you for your feedback!")
303
- time.sleep(1)
304
  st.rerun()
305
  else:
306
- st.warning("Please enter some feedback.")
 
15
  from feedback import save_feedback
16
  from ui_styles import BASE_CSS
17
 
18
+ # --------------------------------------------------
19
+ # Page Config
20
+ # --------------------------------------------------
21
  st.set_page_config(
22
  page_title="AI Skill Swap",
23
  page_icon="🀝",
 
26
 
27
  st.markdown(f"<style>{BASE_CSS}</style>", unsafe_allow_html=True)
28
 
29
+ # --------------------------------------------------
30
+ # Header
31
+ # --------------------------------------------------
32
+ h1, h2 = st.columns([1, 6])
33
 
34
+ with h1:
35
+ st.image(
36
+ "https://via.placeholder.com/100x100/4F46E5/FFFFFF?text=🀝",
37
+ width=90,
38
+ )
 
 
 
39
 
40
+ with h2:
41
+ st.title("AI Skill Swap")
42
+ st.caption("Teach what you know β€’ Learn what you love β€’ Match intelligently")
43
 
 
44
  store = ProfileStore()
45
 
46
+ # --------------------------------------------------
47
+ # SIDEBAR β€” PROFILE MANAGEMENT
48
+ # --------------------------------------------------
49
  with st.sidebar:
50
+ st.header("πŸ‘€ Profile Manager")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
+ profiles = store.load_all()
53
+ usernames = [p.username for p in profiles]
54
+
55
+ selected_user = st.selectbox(
56
+ "Select existing profile",
57
+ ["β€” Create New β€”"] + usernames,
58
+ )
59
+
60
+ st.markdown("### πŸ“ Profile Details")
61
+
62
+ username = st.text_input(
63
+ "Username",
64
+ value="" if selected_user == "β€” Create New β€”" else selected_user,
65
+ )
66
+
67
+ offers_text = st.text_area(
68
+ "Skills you can teach",
69
+ height=90,
70
+ value=st.session_state.get("offers_text", ""),
71
+ placeholder="Python, React, Data Science",
72
+ )
73
+
74
+ wants_text = st.text_area(
75
+ "Skills you want to learn",
76
+ height=90,
77
+ value=st.session_state.get("wants_text", ""),
78
+ placeholder="LLMs, DevOps, UI Design",
79
+ )
80
+
81
+ availability = st.text_input(
82
+ "Availability",
83
+ placeholder="Evenings / Weekends",
84
+ value=st.session_state.get("availability", ""),
85
+ )
86
+
87
+ preferences = st.text_input(
88
+ "Language / Preferences",
89
+ placeholder="English, Hindi, Urdu",
90
+ value=st.session_state.get("preferences", ""),
91
+ )
92
+
93
+ avatar_file = st.file_uploader(
94
+ "Upload Avatar",
95
+ type=["png", "jpg", "jpeg"],
96
+ )
97
 
98
+ st.markdown("---")
99
 
100
+ col_a, col_b = st.columns(2)
 
 
101
 
102
+ # ---------------- CREATE PROFILE ----------------
103
+ with col_a:
104
+ if st.button("βž• Create", use_container_width=True):
105
+ if not username.strip():
106
+ st.error("Username required")
107
+ elif store.find_by_username(username):
108
+ st.error("Profile already exists. Use Update.")
109
+ else:
110
+ avatar_path = None
111
+ if avatar_file:
112
+ safe = re.sub(r"[^A-Za-z0-9_.-]", "_", username)
113
+ ext = Path(avatar_file.name).suffix
114
+ avatar_path = str(
115
+ AVATAR_DIR / f"{safe}_{int(time.time())}{ext}"
116
+ )
117
+ with open(avatar_path, "wb") as f:
118
+ f.write(avatar_file.getbuffer())
119
+
120
+ profile = Profile(
121
+ id=str(uuid.uuid4()),
122
+ username=username.strip(),
123
+ offers=normalize_skill_list(offers_text),
124
+ wants=normalize_skill_list(wants_text),
125
+ availability=availability.strip(),
126
+ preferences=preferences.strip(),
127
+ avatar=avatar_path,
128
  )
129
+
130
+ ok, msg = store.add_or_update(profile)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  st.success(msg)
132
+ st.rerun()
133
+
134
+ # ---------------- UPDATE PROFILE ----------------
135
+ with col_b:
136
+ if st.button("πŸ’Ύ Update", use_container_width=True):
137
+ existing = store.find_by_username(username)
138
+ if not existing:
139
+ st.error("Profile does not exist. Use Create.")
140
  else:
141
+ existing.offers = normalize_skill_list(offers_text)
142
+ existing.wants = normalize_skill_list(wants_text)
143
+ existing.availability = availability.strip()
144
+ existing.preferences = preferences.strip()
145
+
146
+ if avatar_file:
147
+ safe = re.sub(r"[^A-Za-z0-9_.-]", "_", username)
148
+ ext = Path(avatar_file.name).suffix
149
+ avatar_path = str(
150
+ AVATAR_DIR / f"{safe}_{int(time.time())}{ext}"
151
+ )
152
+ with open(avatar_path, "wb") as f:
153
+ f.write(avatar_file.getbuffer())
154
+ existing.avatar = avatar_path
155
 
156
+ store.add_or_update(existing)
157
+ st.success("Profile updated")
158
+ st.rerun()
159
 
160
+ # ---------------- DELETE PROFILE ----------------
161
+ if selected_user != "β€” Create New β€”":
162
+ if st.button("πŸ—‘οΈ Delete Profile", type="secondary"):
163
+ store.delete(selected_user)
164
+ st.warning("Profile deleted")
165
+ time.sleep(0.3)
 
 
 
 
 
 
166
  st.rerun()
167
 
168
+ # --------------------------------------------------
169
+ # MAIN CONTENT
170
+ # --------------------------------------------------
171
+ left, right = st.columns([2, 3])
 
 
 
 
 
 
 
 
 
 
172
 
173
+ # --------------------------------------------------
174
+ # COMMUNITY PROFILES
175
+ # --------------------------------------------------
176
+ with left:
177
+ st.subheader("🌍 Community")
178
 
 
 
 
179
  profiles = store.load_all()
180
 
181
  if not profiles:
182
+ st.info("No profiles yet. Create one from the sidebar.")
183
  else:
184
  for p in profiles:
185
+ st.markdown("<div class='card'>", unsafe_allow_html=True)
186
+ cols = st.columns([1, 4])
187
 
188
+ with cols[0]:
189
  if p.avatar and Path(p.avatar).exists():
190
+ st.image(p.avatar, width=60)
191
  else:
192
+ st.image("https://via.placeholder.com/60", width=60)
 
 
 
193
 
194
+ with cols[1]:
195
  st.markdown(f"**{p.username}**")
 
 
196
  st.caption(f"{p.availability} β€’ {p.preferences}")
197
+ st.markdown(f"🧠 **Offers:** {', '.join(p.offers) or 'β€”'}")
198
+ st.markdown(f"🎯 **Wants:** {', '.join(p.wants) or 'β€”'}")
199
 
200
+ st.markdown("</div>", unsafe_allow_html=True)
201
+
202
+ # --------------------------------------------------
203
+ # AI MATCHMAKING
204
+ # --------------------------------------------------
205
+ with right:
206
+ st.subheader("πŸ€– AI Matchmaking")
207
 
208
  if not profiles:
209
+ st.info("Add profiles to enable matchmaking.")
210
  else:
211
  pick = st.selectbox(
212
+ "Choose your profile",
213
  [p.username for p in profiles],
214
  )
215
 
216
+ if st.button("✨ Find Best Matches"):
217
+ with st.spinner("Analyzing profiles..."):
 
 
218
  current = store.find_by_username(pick)
219
 
 
 
 
 
220
  try:
221
+ sys_msg, user_msg = make_prompt_for_matching(
222
+ current, profiles, 3
 
 
223
  )
224
+ matches = ask_groq_for_matches(sys_msg, user_msg)
225
+ except Exception:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  matches = calculate_local_matches(
227
  current,
228
+ [p for p in profiles if p.id != current.id],
229
+ 3,
230
  )
231
 
 
 
 
 
 
232
  for m in matches:
233
+ st.markdown("<div class='card'>", unsafe_allow_html=True)
234
+ st.markdown(f"### {m.get('username')}")
235
+ score = float(m.get("score", 0))
236
+ st.progress(score / 100)
237
+ st.caption(f"Match Score: {score}%")
238
+ st.caption(m.get("reason", "AI Match"))
239
+ st.markdown("</div>", unsafe_allow_html=True)
240
+
241
+ # --------------------------------------------------
242
+ # FEEDBACK
243
+ # --------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  with st.sidebar:
245
  st.markdown("---")
246
  with st.expander("πŸ’¬ Feedback"):
247
+ fb = st.text_area("Your feedback")
248
+ if st.button("Submit"):
249
+ if fb.strip():
 
250
  save_feedback(
251
  {
252
  "timestamp": time.time(),
253
+ "feedback": fb,
254
+ "username": st.session_state.get("username", "anonymous"),
 
 
 
255
  }
256
  )
257
+ st.success("Thank you!")
 
258
  st.rerun()
259
  else:
260
+ st.warning("Please write feedback.")