β€œbotayla” commited on
Commit
5a604fc
Β·
1 Parent(s): f60fad6
Files changed (1) hide show
  1. src/recommendation/recommender.py +41 -18
src/recommendation/recommender.py CHANGED
@@ -49,22 +49,29 @@ class RecommendationService:
49
  # Step 2: keywords from latest note per category
50
  # ──────────────────────────────────────────────
51
  def _latest_note_per_category(
52
- self, notes: List[Dict], categories: List[str]
53
- ) -> Dict[str, Dict]:
54
  """
55
- For each target category return the note with the largest createdAt value.
56
- Notes are pre-fetched so no extra DB calls needed.
57
  """
58
- latest: Dict[str, Dict] = {}
 
 
59
  for note in notes:
60
  cat = note.get("category")
61
  cats = cat if isinstance(cat, list) else [cat] if cat else []
62
  ts = note.get("createdAt", 0)
 
63
  for c in cats:
64
  if c in categories:
65
- if c not in latest or ts > latest[c].get("createdAt", 0):
66
- latest[c] = note
67
- return latest
 
 
 
 
 
68
 
69
  async def _extract_keywords_with_claude(
70
  self, note: Dict, category: str
@@ -213,23 +220,39 @@ class RecommendationService:
213
  # ── Step 2: keywords via Claude ──────────
214
  latest_notes = self._latest_note_per_category(notes, top_categories)
215
 
216
- keyword_tasks = [
217
- self._extract_keywords_with_claude(latest_notes[cat], cat)
218
- for cat in top_categories
219
- if cat in latest_notes
220
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  keyword_results = await asyncio.gather(*keyword_tasks)
222
 
223
- category_keywords: Dict[str, List[str]] = {
224
- cat: kws
225
- for cat, kws in zip(top_categories, keyword_results)
226
- }
227
 
228
  # ── Step 3: build query & recommend ──────
229
  all_videos = []
230
 
231
  for category, keywords in category_keywords.items():
232
- query = " ".join(keywords[:3])
233
 
234
  logger.info(f"🎯 Searching category: {category} | Query: {query}")
235
 
 
49
  # Step 2: keywords from latest note per category
50
  # ──────────────────────────────────────────────
51
  def _latest_note_per_category(
52
+ self, notes: List[Dict], categories: List[str], top_k: int = 3
53
+ ) -> Dict[str, List[Dict]]:
54
  """
55
+ Return top K latest notes per category instead of a single note.
 
56
  """
57
+
58
+ grouped: Dict[str, List[Dict]] = {c: [] for c in categories}
59
+
60
  for note in notes:
61
  cat = note.get("category")
62
  cats = cat if isinstance(cat, list) else [cat] if cat else []
63
  ts = note.get("createdAt", 0)
64
+
65
  for c in cats:
66
  if c in categories:
67
+ grouped[c].append(note)
68
+
69
+ # sort each category by latest first
70
+ for c in grouped:
71
+ grouped[c].sort(key=lambda x: x.get("createdAt", 0), reverse=True)
72
+ grouped[c] = grouped[c][:top_k]
73
+
74
+ return grouped
75
 
76
  async def _extract_keywords_with_claude(
77
  self, note: Dict, category: str
 
220
  # ── Step 2: keywords via Claude ──────────
221
  latest_notes = self._latest_note_per_category(notes, top_categories)
222
 
223
+ keyword_tasks = []
224
+
225
+ for cat in top_categories:
226
+ if cat not in latest_notes:
227
+ continue
228
+
229
+ notes_list = latest_notes[cat]
230
+
231
+ # Ω†Ψ¬Ω…ΨΉ ΩƒΩ„ Ψ§Ω„Ω†ΩˆΨͺΨ³ في Ω†Ψ΅ واحد
232
+ combined_content = "\n".join(
233
+ n.get("content", "")[:500]
234
+ for n in notes_list
235
+ )
236
+
237
+ fake_note = {
238
+ "content": combined_content
239
+ }
240
+
241
+ keyword_tasks.append(
242
+ self._extract_keywords_with_claude(fake_note, cat)
243
+ )
244
  keyword_results = await asyncio.gather(*keyword_tasks)
245
 
246
+ category_keywords = {}
247
+
248
+ for cat, kws in zip(latest_notes.keys(), keyword_results):
249
+ category_keywords[cat] = kws
250
 
251
  # ── Step 3: build query & recommend ──────
252
  all_videos = []
253
 
254
  for category, keywords in category_keywords.items():
255
+ query = " ".join(keywords[:3]) +" tutorial explanation"
256
 
257
  logger.info(f"🎯 Searching category: {category} | Query: {query}")
258