diff --git a/App/schemas/auth_schema.py b/App/schemas/auth_schema.py index 66c65a6..a5c453c 100644 --- a/App/schemas/auth_schema.py +++ b/App/schemas/auth_schema.py @@ -1,20 +1,28 @@ from pydantic import BaseModel, EmailStr, Field -# [응답용] 프론트엔드 반환용 유저 기본 정보 (보안상 비밀번호 제외) class UserData(BaseModel): - id: int - name: str - email: str + """ + [응답용] 프론트엔드 반환용 유저 기본 정보 (보안상 비밀번호 제외) + """ + id: int = Field(..., description="유저 고유 ID") + name: str = Field(..., description="유저 이름") + email: str = Field(..., description="유저 이메일") -# [내부용] DB 조회 및 서버 내부 인증용 (UserData 상속 + 암호화된 비밀번호) class UserDataPASS(UserData): - hashed_password: str + """ + [내부용] DB 조회 및 서버 내부 인증용 (UserData 상속 + 암호화된 비밀번호) + """ + hashed_password: str = Field(..., description="bcrypt로 해싱된 비밀번호 (서버 내부 인증용, 외부 노출 금지)") -# [요청용] 로그인 API (POST /auth/login) 수신 JSON 데이터 검증 class LoginRequest(BaseModel): - email: EmailStr - password: str = Field(min_length=8, max_length=30) + """ + [요청용] 로그인 API (POST /auth/login) 수신 JSON 데이터 검증 + """ + email: EmailStr = Field(..., description="로그인 이메일") + password: str = Field(min_length=8, max_length=30, description="비밀번호 (8자 이상 30자 이하)") -# [요청용] 회원가입 API (POST /auth/register) 수신 JSON 데이터 검증 class RegisterRequest(LoginRequest): - name: str = Field(min_length=2, max_length=100) \ No newline at end of file + """ + [요청용] 회원가입 API (POST /auth/register) 수신 JSON 데이터 검증 + """ + name: str = Field(min_length=2, max_length=100, description="유저 이름 (2자 이상 100자 이하)") \ No newline at end of file diff --git a/App/schemas/image_schema.py b/App/schemas/image_schema.py index 6909d46..9c66ac8 100644 --- a/App/schemas/image_schema.py +++ b/App/schemas/image_schema.py @@ -1,25 +1,60 @@ -from pydantic import BaseModel from datetime import datetime +from pydantic import BaseModel, Field class BaseMetadata(BaseModel): - image_id: int - image_loc: str - label: str - version_type: str - model_type: str - domain_type: str - created_at: datetime + """ + [공통 메타데이터] 이미지 분석 결과의 기본 식별 정보 + + - routes: image (히스토리 조회) + - services: image_svc.get_image_result, get_user_histories, get_user_history + """ + image_id: int = Field(..., description="이미지 분석 레코드 고유 ID (image_result.id)") + image_loc: str = Field(..., description="서버 내 저장된 이미지 파일 경로 (DB 저장 형식, 예: /static/uploads/user@a.com/img_1700000000.png)") + label: str = Field(..., description="딥페이크 판정 결과 라벨 (FAKE / REAL / UNKNOWN)") + version_type: str = Field(..., description="사용된 모델 버전 (v1 / v2)") + model_type: str = Field(..., description="모델 속도/정확도 모드 (fast / pro)") + domain_type: str = Field(..., description="얼굴 도메인 타입 (서양인 / 동양인)") + created_at: datetime = Field(..., description="분석 요청 생성 시각 (UTC)") + class InferenceResult(BaseModel): - score : float - face_conf : float - face_ratio : float - face_brightness : float - result_msg : str + """ + [추론 상세 결과] 딥페이크 분석 수치 결과 + + - 상속 베이스 클래스 (직접 응답 X) + - 상속처: UserHistory_indi, ImageData_indi + - 사용처: image (GET /image/history/{image_id}), inference (GET /inference/image/{image_id}) + - services: image_svc.get_user_history, image_svc.get_image_result + """ + score: float = Field(..., description="딥페이크 확률 점수 (0.0~1.0, 0.5 이상이면 FAKE 판정). 분석 실패 시 -1.0") + face_conf: float = Field(..., description="얼굴 탐지 신뢰도 (0.0~1.0). 분석 실패 시 -1.0") + face_ratio: float = Field(..., description="이미지 내 얼굴이 차지하는 면적 비율 (0.0~1.0). 분석 실패 시 -1.0") + face_brightness: float = Field(..., description="얼굴 영역 평균 밝기 값. 분석 실패 시 -1.0") + result_msg: str = Field(..., description="분석 결과에 대한 상세 메시지 (성공/경고/실패 사유)") + class UserHistory(BaseMetadata): - user_id: int + """ + [히스토리 목록] 회원 전체 이미지 분석 이력 + + - routes: image (GET /image/history) + - services: image_svc.get_user_histories + """ + user_id: int = Field(..., description="분석을 요청한 유저 ID (user.id FK)") -class UserHistory_indi(UserHistory, InferenceResult): +class UserHistory_indi(UserHistory, InferenceResult): + """ + [히스토리 상세] 회원 개별 이미지 분석 상세 결과 + + - routes: image (GET /image/history/{image_id}, DELETE /image/history/{image_id} - 내부 조회) + - services: image_svc.get_user_history + """ status: str + class ImageData_indi(BaseMetadata, InferenceResult): - user_id : int | None - status : str + """ + [추론 결과 조회] 분석 진행 상태 + 최종 결과 (회원/비회원 공통) + + - routes: inference (GET /inference/image/{image_id}) + - services: image_svc.get_image_result + """ + user_id: int | None = Field(None, description="유저 ID. 비회원 분석 요청의 경우 None") + status: str = Field(..., description="분석 진행 상태 (PENDING / SUCCESS / WARNING / FAILED)", examples=["SUCCESS"]) \ No newline at end of file diff --git a/App/schemas/video_schema.py b/App/schemas/video_schema.py index 7da9f05..1b9c443 100644 --- a/App/schemas/video_schema.py +++ b/App/schemas/video_schema.py @@ -1,50 +1,69 @@ -from pydantic import BaseModel from datetime import datetime +from pydantic import BaseModel, Field -# [히스토리 목록] GET /video/history -# 사용자 전체 비디오 히스토리 조회 class VideoData(BaseModel): - id: int - user_id : int | None - video_loc : str - status : str - label : str - version_type : str - model_type : str - domain_type : str - created_at : datetime + """ + [히스토리 목록] 사용자 전체 비디오 히스토리 조회 + + - routes: video (GET /video/history) + - services: video_svc.get_user_histories + """ + id: int = Field(..., description="비디오 분석 레코드 고유 ID (video_result.id)") + user_id: int | None = Field(None, description="분석 요청 유저 ID. 비회원의 경우 None") + video_loc: str = Field(..., description="서버 내 저장된 비디오 파일 경로 (예: /static/uploads/user@a.com/vid_1700000000.mp4)") + status: str = Field(..., description="분석 진행 상태 (PENDING / SUCCESS / WARNING / FAILED)") + label: str = Field(..., description="딥페이크 판정 결과 라벨 (FAKE / REAL / UNKNOWN)") + version_type: str = Field(..., description="사용된 모델 버전 (v1 / v2)") + model_type: str = Field(..., description="모델 속도/정확도 모드 (fast / pro)") + domain_type: str = Field(..., description="얼굴 도메인 타입 (서양인 / 동양인)") + created_at: datetime = Field(..., description="분석 요청 생성 시각 (UTC)") -# [히스토리 상세 / 추론 결과] GET /video/history/{video_id}, GET /inference/video/{video_id} -# 개별 비디오 상세 조회 + 추론 결과값 포함 class VideoDetailData(VideoData): + """ + [히스토리 상세 / 추론 결과] 개별 비디오 상세 + 추론 수치 결과 - score : float - face_conf : float - face_ratio : float - face_brightness : float - result_msg : str + - routes: video (GET /video/history/{video_id}), inference (GET /inference/video/{video_id}) + - services: video_svc.get_user_history, video_svc.get_video_result + """ + score: float = Field(..., description="비디오 전체 평균 딥페이크 확률 (0.0~1.0). 실패 시 -1.0") + face_conf: float = Field(..., description="평균 얼굴 탐지 신뢰도. 실패 시 -1.0") + face_ratio: float = Field(..., description="평균 얼굴 면적 비율. 실패 시 -1.0") + face_brightness: float = Field(..., description="평균 얼굴 영역 밝기. 실패 시 -1.0") + result_msg: str = Field(..., description="분석 결과 상세 메시지") -# [프레임별 추론 결과] GET /inference/video/{video_id}/detail 응답 내부 -# video_frame_result 테이블 row 단위 매핑 class VideoFrameData(BaseModel): - frame_index: int - frame_time: float - score: float - face_conf: float - face_ratio: float - face_brightness: float + """ + [프레임별 추론 결과] 비디오 프레임 단위 분석 결과 -# [비디오 메타 정보] GET /inference/video/{video_id}/detail 응답 내부 -# video_meta_result 테이블 row 단위 매핑 + - routes: inference (GET /inference/video/{video_id}/detail 응답 내부) + - services: video_svc.get_video_frame_result, save_video_frame_result + """ + frame_index: int = Field(..., description="비디오 내 프레임 인덱스 (0부터 시작)") + frame_time: float = Field(..., description="해당 프레임의 영상 내 재생 시점 (초 단위)") + score: float = Field(..., description="해당 프레임의 딥페이크 확률 점수 (0.0~1.0)") + face_conf: float = Field(..., description="해당 프레임 얼굴 탐지 신뢰도") + face_ratio: float = Field(..., description="해당 프레임 얼굴 면적 비율") + face_brightness: float = Field(..., description="해당 프레임 얼굴 영역 밝기") + class VideoMetaData(BaseModel): - fps: float - total_frames: int # 전체 프레임 수 - num_sampled: int # 샘플링 대상 프레임 수 - num_extracted: int # 실제 추출된 프레임 수 - num_detected: int # 얼굴 탐지 성공 프레임 수 + """ + [비디오 메타 정보] 비디오 전체 프레임 처리 통계 + + - routes: inference (GET /inference/video/{video_id}/detail 응답 내부) + - services: video_svc.get_video_meta_result, save_video_meta_result + """ + fps: float = Field(..., description="비디오의 초당 프레임 수 (Frames Per Second)") + total_frames: int = Field(..., description="비디오 전체 프레임 수") + num_sampled: int = Field(..., description="추론을 위해 샘플링한 프레임 수") + num_extracted: int = Field(..., description="실제로 추출에 성공한 프레임 수") + num_detected: int = Field(..., description="얼굴 탐지에 성공한 프레임 수 (score 산출 성공)") -# [상세 분석 최종 응답] GET /inference/video/{video_id}/{detail} -# 로그인 유저 전용 (get_session_user_prt) class VideoDetailResponse(BaseModel): - meta: VideoMetaData - frames: list[VideoFrameData] \ No newline at end of file + """ + [상세 분석 최종 응답] 비디오 상세 분석 화면용 통합 응답 + + 사용처: + - routes: inference (GET /inference/video/{video_id}/detail) - 로그인 유저 전용 + """ + meta: VideoMetaData = Field(..., description="비디오 메타 정보 (FPS, 프레임 수 등)") + frames: list[VideoFrameData] = Field(..., description="프레임별 상세 분석 결과 리스트 (frame_index 오름차순)") \ No newline at end of file