66from app .emotion .router import predict_emotion
77from app .statistics .models import EmotionStatistics
88from app .user .auth import get_current_user
9- from app .diary .models import Diary
10- from app .diary .schemas import DiaryCreateRequest , DiaryUpdateRequest , DiaryResponse , DiaryCountResponse
9+ from app .diary .models import Diary , RecommendedSong
10+ from app .diary .schemas import DiaryCreateRequest , DiaryUpdateRequest , DiaryResponse , DiaryCountResponse , SongResponse
1111from app .user .models import User
1212from app .embedding .models import kobert , save_diary_embedding , split_sentences , get_user_preferred_genres , \
1313 get_songs_by_genre , get_song_embeddings , calculate_similarity
1818import torch
1919import numpy as np
2020import heapq
21- import torch .nn .functional as F
2221from datetime import datetime
2322
2423router = APIRouter ()
2524
2625logging .basicConfig (level = logging .INFO )
2726logger = logging .getLogger (__name__ )
2827
29- # 📝 일기 작성 API
3028@router .post ("" , response_model = DiaryResponse , status_code = 201 , summary = "일기 작성 & 노래 추천" ,
3129 description = "일기를 작성하면 자동으로 임베딩을 진행하고, 사용자의 선호 장르 내에서 가장 유사한 노래를 추천합니다." )
3230async def create_diary (
@@ -305,6 +303,32 @@ async def create_diary_with_music_recommend_top3(
305303
306304 save_diary_embedding (session , new_diary .id , combined_embedding )
307305
306+ recommended_songs = [
307+ {
308+ "song_id" : match ["song_id" ],
309+ "song_name" : match ["metadata" ]["song_name" ],
310+ "best_lyric" : " " .join (match ["lyric_chunk" ]),
311+ "similarity_score" : round (float (sim ), 4 ),
312+ "album_image" : match ["metadata" ]["album_image" ],
313+ "artist" : match ["metadata" ]["artist" ],
314+ "genre" : match ["metadata" ]["genre" ]
315+ }
316+ for sim , match in top_3
317+ ]
318+
319+ for song_data in recommended_songs :
320+ new_song = RecommendedSong (
321+ diary_id = new_diary .id ,
322+ song_id = song_data ["song_id" ], # MongoDB ID 문자열 변환
323+ song_name = song_data ["song_name" ],
324+ artist = song_data ["artist" ],
325+ genre = song_data ["genre" ],
326+ album_image = song_data ["album_image" ],
327+ best_lyric = song_data ["best_lyric" ],
328+ similarity_score = song_data ["similarity_score" ]
329+ )
330+ session .add (new_song )
331+
308332 # 9-1) 감정 통계 업데이트 또는 추가
309333 existing_stat = session .query (EmotionStatistics ).filter (
310334 EmotionStatistics .user_id == current_user .id ,
@@ -327,20 +351,9 @@ async def create_diary_with_music_recommend_top3(
327351 )
328352 session .add (new_stat )
329353
330- # 10) 응답 구성
331- recommended_songs = [
332- {
333- "song_id" : match ["song_id" ],
334- "song_name" : match ["metadata" ]["song_name" ],
335- "best_lyric" : " " .join (match ["lyric_chunk" ]),
336- "similarity_score" : round (float (sim ), 4 ),
337- "album_image" : match ["metadata" ]["album_image" ],
338- "artist" : match ["metadata" ]["artist" ],
339- "genre" : match ["metadata" ]["genre" ]
340- }
341- for sim , match in top_3
342- ]
354+ session .commit ()
343355
356+ # 10) 응답 구성
344357 response_data = {
345358 "id" : new_diary .id ,
346359 "user_id" : new_diary .user_id ,
@@ -349,7 +362,9 @@ async def create_diary_with_music_recommend_top3(
349362 "confidence" : confidence_full ,
350363 "created_at" : new_diary .created_at ,
351364 "updated_at" : new_diary .updated_at ,
352- "recommended_songs" : recommended_songs
365+ "recommended_songs" : recommended_songs ,
366+ "top_emotions" : [{"emotion_id" : emo_id , "score" : round (score , 4 )}
367+ for emo_id , score in sorted (emotion_vote_counter .items (), key = lambda x : - x [1 ])[:3 ]]
353368 }
354369
355370 logger .info ("추천 결과: %s" , json .dumps (response_data , indent = 2 , ensure_ascii = False , default = str ))
@@ -374,6 +389,33 @@ def get_diary(
374389
375390 return diary
376391
392+ @router .get ("/{diary_id}/recommended-songs" , response_model = list [SongResponse ],
393+ summary = "추천 노래 조회" ,
394+ description = "특정 일기에 대한 추천 노래 리스트를 조회합니다." )
395+ def get_recommended_songs_by_diary (
396+ diary_id : int ,
397+ current_user : User = Depends (get_current_user ),
398+ db : Session = Depends (get_db )
399+ ):
400+ # 1. 해당 일기가 유저의 것인지 검증
401+ diary = db .query (Diary ).filter (
402+ Diary .id == diary_id ,
403+ Diary .user_id == current_user .id
404+ ).first ()
405+
406+ if not diary :
407+ raise HTTPException (status_code = 404 , detail = "일기를 찾을 수 없습니다." )
408+
409+ # 2. 추천곡 조회
410+ songs = db .query (RecommendedSong ).filter (
411+ RecommendedSong .diary_id == diary_id
412+ ).order_by (RecommendedSong .similarity_score .desc ()).all ()
413+
414+ if not songs :
415+ raise HTTPException (status_code = 404 , detail = "추천된 노래가 없습니다." )
416+
417+ return songs
418+
377419@router .get ("" , response_model = List [DiaryResponse ],
378420 summary = "내 일기 목록 조회" ,
379421 description = "로그인한 사용자가 작성한 모든 일기를 조회합니다." )
0 commit comments