Skip to content

Merge to main#11

Merged
H4nnhoi merged 14 commits intomainfrom
develop
Oct 31, 2025
Merged

Merge to main#11
H4nnhoi merged 14 commits intomainfrom
develop

Conversation

@H4nnhoi
Copy link
Contributor

@H4nnhoi H4nnhoi commented Oct 30, 2025

🔎 Description

보호자 api 제작 완료 및 감정 분석 비동기 설정

🔗 Related Issue

🏷️ What type of PR is this?

  • ✨ Feature
  • ♻️ Code Refactor
  • 🐛 Bug Fix
  • 🚑 Hot Fix
  • 📝 Documentation Update
  • 🎨 Style
  • ⚡️ Performance Improvements
  • ✅ Test
  • 👷 CI
  • 💚 CI Fix

📋 Changes Made

  • /cares/users/voices
  • /cares/users/voices/analyzing/frequency
  • /cares/users/voices/analyzing/weekly
  • /questions
  • /questions/random
  • /users/voices/{voiceId}
  • auth by requestParam(username)

🧪 Testing

  • Unit tests pass
  • Integration tests pass
  • Manual testing completed

Summary by CodeRabbit

  • 새로운 기능

    • '놀람' 감정 지표 추가 및 감정 분석 점수에 반영
    • 돌봄(케어)용 월별/주별 감정 빈도 및 요약 조회 API 추가
    • 돌봄 대상자의 음성 목록 조회 및 페이징 지원
    • 사용자 음성 상세 조회·삭제, 질문 목록 및 무작위 질문 API 추가
    • 업로드 시 백그라운드 음성 감정 분석 처리(여러 오디오 포맷 지원 포함)
    • 라우팅 구조 모듈화 및 테스트용 음성 분석 미리보기 엔드포인트 추가
  • 문서

    • 업로드 후 비동기 처리 흐름(즉시 응답 및 이후 조회로 분석 결과 제공) 문서화 추가

@H4nnhoi H4nnhoi self-assigned this Oct 30, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 30, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

음성 업로드 시 즉시 저장/응답하고 STT → 감정분석 → 음성분석이 백그라운드로 비동기 실행되도록 파이프라인·라우터·DB·DTO·서비스 계층을 추가·확장했습니다.

Changes

Cohort / File(s) 요약
문서
README.md
업로드 즉시 응답 후 비동기 처리 흐름(STT → 감정분석 → 음성분석 및 조회 방식) 문서화
감정 분석 서비스
app/emotion_service.py
모델 경로 업데이트(리밸런스), soundfile 우선 로드·librosa 폴백, 한국어→영어 라벨 매핑, 출력에 top_emotion·emotion_scores·샘플레이트·지속시간 등 확장, 모듈 레벨 emotion_analyzer 인스턴스 추가
STT·오디오 로딩
app/stt_service.py
업로드 확장자 기반 임시파일 유지, robust_load 함수로 soundfile 우선 로드·librosa 폴백 및 리샘플링, 임시파일 정리 유지
음성 서비스(업로드/백그라운드)
app/voice_service.py
업로드 시 _process_audio_emotion_background 비동기 작업 추가(감정분석→정규화→DB 저장), STT/NLP 파이프라인과 병렬 스케줄링, 조회/삭제 API 및 응답에 voice_id 포함
보호자 서비스 계층
app/care_service.py
신규 CareService 클래스 추가: get_emotion_monthly_frequency, get_emotion_weekly_summary(검증·집계 로직 포함)
데이터베이스 서비스
app/db_service.py
get_user_by_user_code, get_voice_detail_for_username, get_care_voices, get_voice_owned_by_username, delete_voice_with_relations 등 추가 및 create/update_voice_analyzesurprise_bps 지원
데이터 모델
app/models.py
VoiceAnalyze.surprise_bps 필드 추가 및 CheckConstraint(범위·합산) 업데이트
DTOs
app/dto.py
VoiceListItem.voice_id 추가, CareVoiceListItem, CareUserVoiceListResponse, UserVoiceDetailResponse, VoiceAnalyzePreviewResponse 등 응답 DTO 추가
엔트리·라우터 재구성
app/main.py
여러 APIRouter 도입(users, care, admin, nlp, test, questions), 엔드포인트 재배치·확장 및 CareService 주입

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant API as "API (FastAPI)"
    participant VoiceSvc as "VoiceService"
    participant DB as "DBService / DB"
    participant BG as "Background Task"
    participant Emotion as "EmotionService"

    User->>API: POST /users/voices (파일 업로드)
    API->>VoiceSvc: upload_user_voice(file, ...)
    VoiceSvc->>DB: create_voice() → voice_id
    VoiceSvc->>BG: schedule _process_audio_emotion_background(file, voice_id)
    API-->>User: 202 Accepted (voice_id)

    Note over BG,Emotion: 비동기 파이프라인 (STT → 감정분석 → 음성분석 → DB 저장)
    BG->>Emotion: analyze_voice_emotion(tempfile)
    Emotion-->>BG: emotion scores (raw)
    BG->>DB: create_voice_analyze(normalized scores, top_emotion)

    User->>API: GET /users/voices/{voice_id}
    API->>VoiceSvc: get_user_voice_detail(voice_id)
    VoiceSvc->>DB: get_voice_detail_for_username(voice_id, username)
    DB-->>VoiceSvc: voice + analyze
    API-->>User: UserVoiceDetailResponse
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • 집중 검토 권고:
    • app/emotion_service.py: 오디오 I/O 변경, 라벨 매핑 및 출력 스키마
    • app/voice_service.py: 백그라운드 작업 동시성·예외 처리·DB 일관성
    • app/db_service.py: 관계 삭제(delete_voice_with_relations)와 쿼리(ownership 검증, joinedload)
    • app/models.py: CheckConstraint 변경에 따른 마이그레이션 영향
    • app/main.py: 라우터 재구성에 따른 경로·의존성 주입

Possibly related PRs

  • Merge to main #11 — 동일 기능 집합(보호자 API, 감정분석 백그라운드, DB/DTO/라우터 변경)과 강한 코드 수준 연관성 있음
  • [Feat] voice to text by stt #6app/emotion_service.py 및 오디오 로드 관련 변경(EmotionAnalyzer·robust load)으로 직접적인 연관성 있음

Poem

🐰 나는 귀가 길고 발이 가벼워
업로드 소리 맡아 조용히 달려가요
백그라운드에 감정 씨앗 심고
점수들로 기록을 남기네
당근 들고 축하해요, 훌륭한 배포 🌿🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.14% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive PR 제목 "Merge to main"은 완전히 일반적이고 비특정적이어서 변경 사항의 의미 있는 정보를 전달하지 못합니다. 실제 변경 사항은 보호자 API 완성, 감정 분석 비동기 처리, 새로운 엔드포인트 추가 등 상당한 범위의 기능 개선을 포함하고 있으나, 제목은 이러한 주요 변경 사항을 어떤 방식으로도 반영하지 않습니다. 제목은 모든 PR에 적용될 수 있는 절차적 문구이며, 개발자의 관점에서 주요 변경 사항을 강조하지 못하고 있습니다. PR 제목을 더 구체적이고 설명적인 내용으로 변경하는 것을 권장합니다. 예를 들어 "보호자 API 완성 및 감정 분석 비동기 처리" 또는 "감정 분석 비동기 설정 및 보호자 관련 엔드포인트 추가"와 같이 실제 변경 사항의 핵심을 명확히 드러내는 제목으로 수정하면 좋습니다.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed PR 설명은 필수 섹션 대부분을 포함하고 있습니다. 🔎 Description, 🔗 Related Issue, 🏷️ What type of PR, 📋 Changes Made, 🧪 Testing 섹션이 모두 채워져 있으며, 각 섹션이 적절한 정보를 포함하고 있습니다. 변경 사항 목록에서는 구현된 7개 엔드포인트와 기능이 명확하게 기록되어 있으며, 관련 이슈 링크도 제공되었습니다. 선택 사항인 스크린샷과 추가 노트 섹션이 비어 있지만, 이는 현재 PR의 성격상 필수적이지 않습니다.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b8669b4 and b06aaa8.

📒 Files selected for processing (1)
  • app/emotion_service.py (5 hunks)

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/voice_service.py (1)

157-224: 이벤트 루프 블로킹 최소화 필요
현재 _process_audio_emotion_background에서는 동기 함수인 analyze_voice_emotion을 그대로 호출하고 있어, 파일 길이에 따라 asyncio 이벤트 루프가 수 초 이상 멈출 수 있습니다. 다른 요청도 같은 루프를 공유하므로 TPS 저하로 이어질 가능성이 큽니다. await asyncio.to_thread(...)로 추론 부분만 별도 스레드에서 실행해 루프를 즉시 반환하도록 조정해 주세요.

다음과 같이 변경하면 해결됩니다:

-            result = analyze_voice_emotion(emotion_file)
+            result = await asyncio.to_thread(analyze_voice_emotion, emotion_file)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 90c724f and b8669b4.

📒 Files selected for processing (9)
  • README.md (1 hunks)
  • app/care_service.py (1 hunks)
  • app/db_service.py (8 hunks)
  • app/dto.py (2 hunks)
  • app/emotion_service.py (5 hunks)
  • app/main.py (4 hunks)
  • app/models.py (2 hunks)
  • app/stt_service.py (2 hunks)
  • app/voice_service.py (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
app/care_service.py (3)
app/models.py (3)
  • User (8-29)
  • Voice (32-56)
  • VoiceAnalyze (83-108)
app/auth_service.py (1)
  • get_auth_service (207-209)
app/main.py (2)
  • get_emotion_monthly_frequency (225-233)
  • get_emotion_weekly_summary (236-244)
app/voice_service.py (3)
app/emotion_service.py (1)
  • analyze_voice_emotion (171-173)
app/main.py (2)
  • get_user_voice_detail (146-158)
  • delete_user_voice (161-167)
app/db_service.py (5)
  • create_voice_analyze (170-190)
  • get_care_voices (92-113)
  • get_voice_detail_for_username (66-79)
  • get_voice_owned_by_username (278-285)
  • delete_voice_with_relations (287-298)
app/db_service.py (2)
app/models.py (5)
  • User (8-29)
  • Voice (32-56)
  • VoiceAnalyze (83-108)
  • VoiceQuestion (129-141)
  • VoiceContent (59-80)
app/auth_service.py (1)
  • get_user_by_username (144-146)
app/main.py (7)
app/models.py (1)
  • Question (111-126)
app/auth_service.py (1)
  • get_auth_service (207-209)
app/voice_service.py (5)
  • get_user_voice_list (228-288)
  • get_user_voice_detail (307-336)
  • delete_user_voice (338-350)
  • upload_voice_with_question (352-434)
  • get_care_voice_list (290-305)
app/dto.py (4)
  • UserVoiceListResponse (73-75)
  • UserVoiceDetailResponse (89-94)
  • CareUserVoiceListResponse (84-86)
  • VoiceAnalyzePreviewResponse (97-107)
app/care_service.py (3)
  • CareService (7-103)
  • get_emotion_monthly_frequency (12-44)
  • get_emotion_weekly_summary (46-103)
app/database.py (1)
  • get_db (38-44)
app/emotion_service.py (1)
  • analyze_voice_emotion (171-173)
🪛 Ruff (0.14.2)
app/emotion_service.py

73-73: Consider moving this statement to an else block

(TRY300)


74-74: Do not catch blind exception: Exception

(BLE001)

app/care_service.py

28-28: Do not catch blind exception: Exception

(BLE001)


42-42: Consider moving this statement to an else block

(TRY300)


43-43: Do not catch blind exception: Exception

(BLE001)


44-44: Use explicit conversion flag

Replace with conversion flag

(RUF010)


63-63: Do not catch blind exception: Exception

(BLE001)


101-101: Consider moving this statement to an else block

(TRY300)


102-102: Do not catch blind exception: Exception

(BLE001)


103-103: Use explicit conversion flag

Replace with conversion flag

(RUF010)

app/voice_service.py

82-82: Store a reference to the return value of asyncio.create_task

(RUF006)


83-83: Store a reference to the return value of asyncio.create_task

(RUF006)


162-162: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


163-163: Do not catch blind exception: Exception

(BLE001)


184-184: f-string without any placeholders

Remove extraneous f prefix

(F541)


194-194: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


195-195: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


196-196: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


197-197: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


198-198: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


199-199: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


225-225: Do not catch blind exception: Exception

(BLE001)


303-303: Consider moving this statement to an else block

(TRY300)


304-304: Do not catch blind exception: Exception

(BLE001)


328-334: Consider moving this statement to an else block

(TRY300)


335-335: Do not catch blind exception: Exception

(BLE001)


348-348: Consider moving this statement to an else block

(TRY300)


349-349: Do not catch blind exception: Exception

(BLE001)


350-350: Use explicit conversion flag

Replace with conversion flag

(RUF010)


417-417: Store a reference to the return value of asyncio.create_task

(RUF006)


418-418: Store a reference to the return value of asyncio.create_task

(RUF006)

app/stt_service.py

80-80: Consider moving this statement to an else block

(TRY300)


81-81: Do not catch blind exception: Exception

(BLE001)

app/main.py

52-52: Consider moving this statement to an else block

(TRY300)


53-53: Do not catch blind exception: Exception

(BLE001)


54-54: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


54-54: Use explicit conversion flag

Replace with conversion flag

(RUF010)


79-79: Do not catch blind exception: Exception

(BLE001)


80-80: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


80-80: Use explicit conversion flag

Replace with conversion flag

(RUF010)


91-91: Do not catch blind exception: Exception

(BLE001)


92-92: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


92-92: Use explicit conversion flag

Replace with conversion flag

(RUF010)


171-171: Do not perform function call File in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


173-173: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


208-208: Redefinition of unused random from line 27

Remove definition: random

(F811)


209-209: Standard pseudo-random generators are not suitable for cryptographic purposes

(S311)


277-277: Do not perform function call File in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


291-291: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


292-292: Do not catch blind exception: Exception

(BLE001)


307-307: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


308-308: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


309-309: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


310-310: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


311-311: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


312-312: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)


335-335: Do not catch blind exception: Exception

(BLE001)


336-336: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


336-336: Use explicit conversion flag

Replace with conversion flag

(RUF010)

@H4nnhoi H4nnhoi merged commit 20fd47e into main Oct 31, 2025
This was referenced Oct 31, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant