โ€œbotaylaโ€ commited on
Commit
bb45513
ยท
1 Parent(s): dfcca37

update recommendation

Browse files
src/api/recommendation_routes.py CHANGED
@@ -1,40 +1,28 @@
1
- # from fastapi import APIRouter
2
- # from typing import Dict, Any
3
-
4
- # router = APIRouter(prefix="/recommendations", tags=["Recommendations"])
5
-
6
- # @router.get("")
7
- # async def get_general_recommendations() -> Dict[str, Any]:
8
- # """
9
- # Placeholder endpoint for future ML recommendation logic.
10
- # """
11
- # return {
12
- # "status": "success",
13
- # "recommendations": []
14
- # }
15
-
16
-
17
- from fastapi import APIRouter
18
  from typing import Dict, Any
 
 
 
 
19
 
 
20
  router = APIRouter(prefix="/recommendations", tags=["Recommendations"])
 
21
 
22
  @router.get("")
23
- async def get_general_recommendations() -> Dict[str, Any]:
24
- """
25
- Placeholder endpoint for future ML recommendation logic.
26
- """
 
 
 
 
 
 
 
 
27
  return {
28
  "status": "success",
29
- "recommendations": [
30
- {
31
- "id": "vS07S_yIn_U",
32
- "title": "Introduction to Neural Networks",
33
- "description": "Learn the basics of Neural Networks in this educational video.",
34
- "thumbnail": "https://i.ytimg.com/vi/vS07S_yIn_U/mqdefault.jpg",
35
- "channelTitle": "AI Academy",
36
- "url": "https://www.youtube.com/watch?v=vS07S_yIn_U",
37
- "type": "youtube_video"
38
- }
39
- ]
40
  }
 
1
+ from fastapi import APIRouter, Depends
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  from typing import Dict, Any
3
+ from src.auth.dependencies import get_current_user
4
+ from src.db.firebase import get_firebase_db
5
+ from src.recommendation.recommender import RecommendationService
6
+ from src.utils.logger import setup_logger
7
 
8
+ logger = setup_logger(__name__)
9
  router = APIRouter(prefix="/recommendations", tags=["Recommendations"])
10
+ recommendation_service = RecommendationService()
11
 
12
  @router.get("")
13
+ async def get_general_recommendations(
14
+ current_user=Depends(get_current_user),
15
+ db=Depends(get_firebase_db)
16
+ ) -> Dict[str, Any]:
17
+ logger.info(f"๐ŸŽฏ Getting recommendations for user: {current_user['uid']}")
18
+
19
+ recommendations = await recommendation_service.get_recommendations_for_user(
20
+ db=db,
21
+ user_id=current_user['uid'],
22
+ limit=5
23
+ )
24
+
25
  return {
26
  "status": "success",
27
+ "recommendations": recommendations
 
 
 
 
 
 
 
 
 
 
28
  }
src/recommendation/recommender.py CHANGED
@@ -1,86 +1,80 @@
1
  import asyncio
2
  from typing import List, Dict, Optional
3
- from google import genai
4
  from googleapiclient.discovery import build
5
- from sqlmodel import select
6
- from sqlmodel.ext.asyncio.session import AsyncSession
7
-
8
- from src.db.models import Note
9
  from src.utils.logger import setup_logger
10
- from src.utils.config import settings
11
 
12
  import os
13
  from dotenv import load_dotenv
14
  load_dotenv()
15
 
16
-
17
  logger = setup_logger(__name__)
18
 
19
 
20
  class RecommendationService:
21
  """
22
- Service for suggesting videos and categories based on user's saved notes.
23
- Uses keyword overlap and YouTube Search API for new recommendations.
24
  """
25
 
26
  def __init__(self, api_key: Optional[str] = None):
27
-
28
  self.api_key = "AIzaSyA3erB-Lxd5SOoBOXaumOCVaEr3TcgYG60"
29
- # Use the newer google-genai client
30
- # self.client = genai.Client(api_key=self.api_key)
31
  self.youtube = build("youtube", "v3", developerKey=self.api_key)
32
 
33
  async def get_recommendations_for_user(
34
- self, session: AsyncSession, user_id: int, limit: int = 5
35
  ) -> List[Dict]:
36
  """
37
- Get general recommendations (Categories & YouTube Videos) for a user based on their history.
38
  """
39
- # 1. Fetch user's saved notes
40
- statement = (
41
- select(Note).where(Note.user_id == user_id).order_by(Note.created_at.desc())
42
- )
43
- result = await session.exec(statement)
44
- notes = result.all()
45
 
46
- if not notes:
47
- return await self.get_youtube_recommendations(
48
- "educational tutorials", limit
 
 
 
49
  )
 
 
 
 
 
 
 
 
 
 
50
 
51
- # 2. Extract topics from categories of recent notes
52
  topics = [
53
- n.category
54
  for n in notes[:5]
55
- if n.category and n.category != "Uncategorized"
56
  ]
57
 
58
- # If no categories, use titles
59
  if not topics:
60
- topics = [n.video_title for n in notes[:3]]
61
 
62
  search_query = " ".join(topics[:3])
 
63
 
64
- # 3. Get YouTube recommendations
65
- youtube_recs = await self.get_youtube_recommendations(search_query, limit)
66
-
67
- return youtube_recs
68
 
69
  async def get_youtube_recommendations(
70
  self, query: str, limit: int = 5
71
  ) -> List[Dict]:
72
  """
73
- Search YouTube for new videos based on a query.
74
- Prioritizes educational content.
75
  """
76
  if not query:
77
  return []
78
 
79
- # Enhance query for better educational results
80
  enhanced_query = f"{query} educational lecture tutorial"
 
81
 
82
  try:
83
- # Run in thread pool since google-api-python-client is synchronous
84
  loop = asyncio.get_event_loop()
85
  search_response = await loop.run_in_executor(
86
  None,
@@ -110,37 +104,11 @@ class RecommendationService:
110
  "type": "youtube_video",
111
  }
112
  )
113
- # โœจ ุงู„ุณุทุฑ ุฏู‡ ุงู„ู„ูŠ ู‡ูŠุธู‡ุฑู„ูƒ ุงู„ููŠุฏูŠูˆ ููŠ ุงู„ู€ Logs
114
- logger.info(f"โœ… Found Video: {snippet['title']}")
115
 
116
- # โœจ ุงู„ุณุทุฑ ุฏู‡ ุนุดุงู† ุชุนุฑููŠ ุฅุฌู…ุงู„ูŠ ุงู„ู„ูŠ ุฑุฌุน ูƒุงู… ููŠุฏูŠูˆ
117
  logger.info(f"๐Ÿš€ Total videos fetched: {len(videos)}")
118
  return videos
119
- except Exception as e:
120
- logger.error(f"YouTube search failed: {e}")
121
- return []
122
 
123
- async def get_similar_notes(
124
- self, session: AsyncSession, note_id: int, limit: int = 3
125
- ) -> List[Note]:
126
- """
127
- Find notes similar to a specific note based on category.
128
- """
129
- # Fetch the target note
130
- target_note = await session.get(Note, note_id)
131
- if not target_note:
132
- return []
133
-
134
- # Fetch other notes in the same category
135
- statement = (
136
- select(Note)
137
- .where(
138
- Note.id != note_id,
139
- Note.category == target_note.category,
140
- Note.user_id == target_note.user_id,
141
- )
142
- .limit(limit)
143
- )
144
-
145
- result = await session.exec(statement)
146
- return result.all()
 
1
  import asyncio
2
  from typing import List, Dict, Optional
 
3
  from googleapiclient.discovery import build
 
 
 
 
4
  from src.utils.logger import setup_logger
 
5
 
6
  import os
7
  from dotenv import load_dotenv
8
  load_dotenv()
9
 
 
10
  logger = setup_logger(__name__)
11
 
12
 
13
  class RecommendationService:
14
  """
15
+ Service for suggesting videos based on user's saved notes.
16
+ Uses YouTube Search API for recommendations.
17
  """
18
 
19
  def __init__(self, api_key: Optional[str] = None):
 
20
  self.api_key = "AIzaSyA3erB-Lxd5SOoBOXaumOCVaEr3TcgYG60"
 
 
21
  self.youtube = build("youtube", "v3", developerKey=self.api_key)
22
 
23
  async def get_recommendations_for_user(
24
+ self, db, user_id: str, limit: int = 5
25
  ) -> List[Dict]:
26
  """
27
+ Get recommendations based on user's note history in Firebase.
28
  """
29
+ logger.info(f"๐Ÿ“š Fetching notes for user: {user_id}")
 
 
 
 
 
30
 
31
+ try:
32
+ notes_ref = (
33
+ db.collection("notes")
34
+ .where("user_id", "==", user_id)
35
+ .order_by("created_at", direction="DESCENDING")
36
+ .limit(5)
37
  )
38
+ notes_docs = notes_ref.stream()
39
+ notes = [doc.to_dict() for doc in notes_docs]
40
+ logger.info(f"๐Ÿ“ Found {len(notes)} notes for user")
41
+ except Exception as e:
42
+ logger.error(f"โŒ Failed to fetch notes from Firebase: {e}")
43
+ notes = []
44
+
45
+ if not notes:
46
+ logger.info("โš ๏ธ No notes found, returning general recommendations")
47
+ return await self.get_youtube_recommendations("educational tutorials", limit)
48
 
49
+ # Extract topics from note categories
50
  topics = [
51
+ n.get("category")
52
  for n in notes[:5]
53
+ if n.get("category") and n.get("category") != "Uncategorized"
54
  ]
55
 
56
+ # If no categories found, fall back to video titles
57
  if not topics:
58
+ topics = [n.get("video_title", "") for n in notes[:3]]
59
 
60
  search_query = " ".join(topics[:3])
61
+ logger.info(f"๐Ÿ” Search query built: {search_query}")
62
 
63
+ return await self.get_youtube_recommendations(search_query, limit)
 
 
 
64
 
65
  async def get_youtube_recommendations(
66
  self, query: str, limit: int = 5
67
  ) -> List[Dict]:
68
  """
69
+ Search YouTube for videos based on a query.
 
70
  """
71
  if not query:
72
  return []
73
 
 
74
  enhanced_query = f"{query} educational lecture tutorial"
75
+ logger.info(f"๐ŸŽฌ Searching YouTube for: {enhanced_query}")
76
 
77
  try:
 
78
  loop = asyncio.get_event_loop()
79
  search_response = await loop.run_in_executor(
80
  None,
 
104
  "type": "youtube_video",
105
  }
106
  )
107
+ logger.info(f"โœ… Found video: {snippet['title']}")
 
108
 
 
109
  logger.info(f"๐Ÿš€ Total videos fetched: {len(videos)}")
110
  return videos
 
 
 
111
 
112
+ except Exception as e:
113
+ logger.error(f"โŒ YouTube search failed: {e}")
114
+ return []