Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
language: "ko-KR"

reviews:
profile: "chill"
request_changes_workflow: false
high_level_summary: true
poem: false
collapse_walkthrough: false
auto_review:
enabled: true
drafts: false
path_filters:
- "!**/build/**"
- "!**/.venv/**"
- "!**/node_modules/**"
- "!ai-server/exercise_pb2.py"
- "!ai-server/exercise_pb2_grpc.py"
- "!ai-server/app/proto/exercise_pb2.py"
- "!ai-server/app/proto/exercise_pb2_grpc.py"
- "!mysql/**"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== .coderabbit.yaml =="
cat -n .coderabbit.yaml

echo
echo "== Search for CodeRabbit config docs or related mentions =="
rg -n "coderabbit|exclude|include|path guidance|guidance|mysql/\*" -S . \
  --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' || true

Repository: Shadowfit/init

Length of output: 4826


🌐 Web query:

CodeRabbit path_filters path_instructions exclude include order documentation

💡 Result:

CodeRabbit provides two distinct path-based configuration mechanisms in the.coderabbit.yaml file: path_filters and path_instructions [1][2]. These settings are used to control which files are reviewed and how they are reviewed [1][3]. Path Filters The path_filters setting determines which files CodeRabbit includes in or excludes from the review process [1][3]. - Mechanism: It uses standard glob patterns to manage the scope of reviews [3]. - Exclusion: Patterns prefixed with an exclamation mark (!) are excluded from review (e.g.,!/dist/) [1][3]. - Inclusion: Patterns without the prefix explicitly include files [1][3]. If explicit inclusion patterns are used, CodeRabbit limits its review scope to only those matching patterns, effectively ignoring everything else [3]. - Purpose: These filters are primarily used to exclude irrelevant files like binaries, lock files, generated code, and assets, ensuring the AI focuses on meaningful code [1][2][3]. Path Instructions The path_instructions setting applies targeted, free-text guidance to specific paths [1][4]. - Mechanism: It maps a glob pattern (path) to specific instructions [1][2]. - Purpose: This allows you to enforce custom rules or provide context-specific guidance based on the location of the file [1][2]. For example, you can require specific security checks for API controllers, enforce testing best practices for a test directory, or ensure clarity in documentation [1][2]. Configuration Order The documentation specifies these as separate fields under the reviews configuration block [1][3]. While CodeRabbit uses a hierarchical configuration system (combining repository YAML, central configuration, and defaults), explicit settings in your local.coderabbit.yaml file take precedence [5]. You can verify your resolved configuration at any time by using the @coderabbitai configuration command on a pull request [5]. Top Results: [1] https://docs.coderabbit.ai/configuration/path-instructions [2] https://coderabbit.mintlify.app/configuration/path-instructions [3] https://aicodereview.cc/blog/coderabbit-configuration/

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== mysql-related files =="
fd -t f '^' mysql . 2>/dev/null || true

echo
echo "== paths under mysql at repo root =="
find mysql -maxdepth 3 -type f 2>/dev/null | sed 's#^\./##' | sort || true

Repository: Shadowfit/init

Length of output: 19240


mysql/** 제외와 mysql/*.sql 지침이 서로 무력화됩니다.

mysql/**가 리뷰 대상에서 빠지면 mysql/schema.sqlmysql/data.sql에 적용하려는 지침도 함께 적용되지 않습니다. 스키마/시드까지 검토할 의도라면 제외 범위를 mysql/*.cnf 같은 비-SQL 파일로 좁히세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.coderabbit.yaml at line 20, `.coderabbit.yaml`의 `mysql/**` 제외 패턴을 제거하거나
`mysql/*.cnf`처럼 비-SQL 파일만 제외하도록 좁혀 `mysql/schema.sql`과 `mysql/data.sql`에 대한 기존
리뷰 지침이 적용되게 수정하세요.

path_instructions:
- path: "backend/**/*.java"
instructions: >
Spring Boot 백엔드. 프로덕션 수준으로 리뷰: 트랜잭션 경계, N+1, 인덱스 활용,
동시성(낙관적 락 등), 예외 처리 일관성, 입력 검증을 중점적으로 본다.
- path: "ai-server/**/*.py"
instructions: >
FastAPI/AI 서버. 이 프로젝트에서는 변경 면적을 최소화하는 방침이라, 리팩터링이나
스타일 변경보다 실제 동작 변경 여부와 부작용(특히 세션 상태·gRPC 계약)에 집중해서 본다.
- path: "frontend/**/*.{ts,tsx}"
instructions: >
React Native 프론트엔드. API 계약 일치 여부, 에러 처리, 불필요한 리렌더링을 본다.
- path: "mysql/*.sql"
instructions: >
스키마/시드 데이터. schema.sql과 data.sql의 컬럼 정의가 어긋나지 않는지,
FK의 ON DELETE 정책이 실제 삭제 흐름과 맞는지 확인한다.

chat:
auto_reply: true
13 changes: 10 additions & 3 deletions ai-server/app/core/squat_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
from app.core.dtw_calculator import compute_sync_rate
from app.models.pose import Landmark
from app.models.video import SquatAnalysisResult, SquatFrameMetrics
from app.utils.constants import LANDMARK
from app.utils.constants import LANDMARK, SYNC_THRESHOLDS

# 즉시 수정 필요 컷 / 양호 컷 비율. 기존 고정값(양호>=70, 즉시수정<40)의 비율을 유지한 채
# 페르소나별 "양호" 기준(SYNC_THRESHOLDS)에 비례 스케일한다.
_LOW_CUT_RATIO = 40 / 70


@dataclass
Expand Down Expand Up @@ -313,9 +317,12 @@ def _summarize_rep(self, state, last_raw) -> StreamingRepEvent:
torsos = [last_raw.torso_tilt] if not state.current_rep_frames else [last_raw.torso_tilt]
mean_torso = round(sum(torsos) / len(torsos), 2)

if sync_rate >= 70:
pass_threshold = SYNC_THRESHOLDS.get(state.persona, SYNC_THRESHOLDS["BEGINNER"])
low_threshold = pass_threshold * _LOW_CUT_RATIO

if sync_rate >= pass_threshold:
msg = "자세 양호"
elif sync_rate >= 40:
elif sync_rate >= low_threshold:
msg = "자세 보정 필요"
else:
msg = "즉시 자세 수정 필요"
Expand Down
5 changes: 4 additions & 1 deletion ai-server/app/grpc/exercise_servicer.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,12 @@ def StartAnalysis(self, request, context):
"""[Spring → FastAPI] 운동 분석 세션 시작."""
session_id = request.session_id
exercise_id = request.exercise_id
persona = request.persona or "BEGINNER"
logger.info(
"[Spring → AI] StartAnalysis 수신 (session=%s, exercise=%s, reference_frames=%d)",
"[Spring → AI] StartAnalysis 수신 (session=%s, exercise=%s, persona=%s, reference_frames=%d)",
session_id,
exercise_id,
persona,
len(request.reference_poses),
)

Expand All @@ -83,6 +85,7 @@ def StartAnalysis(self, request, context):
exercise_id=exercise_id,
reference_angles=reference_angles,
exercise_type=exercise_type,
persona=persona,
)

now = Timestamp()
Expand Down
3 changes: 3 additions & 0 deletions ai-server/app/grpc/session_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class SessionState:
session_id: int
exercise_id: int
exercise_type: str = "squat"
persona: str = "BEGINNER"
reference_angles: list[list[float]] = field(default_factory=list)

# 진행 중인 rep에 누적되는 프레임들
Expand Down Expand Up @@ -63,12 +64,14 @@ def create(
exercise_id: int,
reference_angles: list[list[float]],
exercise_type: str = "squat",
persona: str = "BEGINNER",
) -> SessionState:
with self._lock:
state = SessionState(
session_id=session_id,
exercise_id=exercise_id,
exercise_type=exercise_type,
persona=persona,
reference_angles=reference_angles,
)
self._sessions[session_id] = state
Expand Down
2 changes: 2 additions & 0 deletions ai-server/app/proto/exercise.proto
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ message AnalyzeRequest {
string reference_source = 2;
// [핵심] 스프링 DB에서 조회한 기준 좌표 리스트를 여기에 담아서 보냅니다.
repeated PoseDataRequest reference_poses = 4;
// 페르소나별 싱크로율 임계값 계산용 (BEGINNER/ADVANCED/DIET/REHAB). 빈 문자열이면 AI측에서 BEGINNER로 취급.
string persona = 5;
}

message AnalyzeResponse {
Expand Down
10 changes: 5 additions & 5 deletions ai-server/app/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@
],
}

# 동기화율 임계값 (페르소나별)
# 동기화율 임계값 (페르소나별). sync_rate와 동일하게 0~100 스케일.
SYNC_THRESHOLDS = {
"BEGINNER": 0.60,
"ADVANCED": 0.85,
"DIET": 0.70,
"REHAB": 0.50,
"BEGINNER": 60.0,
"ADVANCED": 85.0,
"DIET": 70.0,
"REHAB": 50.0,
}
58 changes: 29 additions & 29 deletions ai-server/exercise_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class AdminExerciseController {
private final AdminExerciseService adminExerciseService;

@Operation(summary = "운동 싱크로율 임계값 변경",
description = "초보자/고급자 임계값을 즉시 갱신. 신규 세션부터 적용. beginner < advanced 필수.")
description = "4개 페르소나(초보자/고급자/다이어트/재활) 임계값을 즉시 갱신. 신규 세션부터 적용. beginner < advanced 필수.")
@PatchMapping("/{exerciseId}/thresholds")
public ResponseEntity<ExerciseThresholdResponseDto> updateThresholds(
@PathVariable Long exerciseId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ public record ExerciseThresholdResponseDto(
Long exerciseId,
String name,
BigDecimal syncThresholdBeginner,
BigDecimal syncThresholdAdvanced
BigDecimal syncThresholdAdvanced,
BigDecimal syncThresholdDiet,
BigDecimal syncThresholdRehab
) {
public static ExerciseThresholdResponseDto fromEntity(Exercise e) {
return new ExerciseThresholdResponseDto(
e.getId(),
e.getName(),
e.getSyncThresholdBeginner(),
e.getSyncThresholdAdvanced()
e.getSyncThresholdAdvanced(),
e.getSyncThresholdDiet(),
e.getSyncThresholdRehab()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ public record ThresholdUpdateDto(
@NotNull
@DecimalMin(value = "0.0", message = "고급자 임계값은 0 이상이어야 합니다")
@DecimalMax(value = "100.0", message = "고급자 임계값은 100 이하여야 합니다")
BigDecimal advanced
BigDecimal advanced,

@NotNull
@DecimalMin(value = "0.0", message = "다이어트 임계값은 0 이상이어야 합니다")
@DecimalMax(value = "100.0", message = "다이어트 임계값은 100 이하여야 합니다")
BigDecimal diet,

@NotNull
@DecimalMin(value = "0.0", message = "재활 임계값은 0 이상이어야 합니다")
@DecimalMax(value = "100.0", message = "재활 임계값은 100 이하여야 합니다")
BigDecimal rehab
Comment on lines +20 to +28

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,160p' backend/src/main/java/com/shadowfit/dto/admin/ThresholdUpdateDto.java
printf '\n---\n'
sed -n '1,140p' backend/src/main/java/com/shadowfit/model/exercise/Exercise.java
printf '\n---\n'
rg -n "DecimalMin|DecimalMax|Digits|scale = 2|diet|rehab|beginner|advanced" backend/src/main/java/com/shadowfit -S

Repository: Shadowfit/init

Length of output: 8304


ThresholdUpdateDto의 소수 자릿수를 scale=2와 맞추세요
beginner/advanced/diet/rehab 모두 @DecimalMin/@DecimalMax만 있어서 70.999 같은 값이 통과합니다. Exercise.syncThreshold* 컬럼은 precision = 5, scale = 2라 저장 시 반올림되거나 거부될 수 있으니, 네 필드에 @Digits(integer = 3, fraction = 2)를 추가하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/main/java/com/shadowfit/dto/admin/ThresholdUpdateDto.java` around
lines 20 - 28, Update ThresholdUpdateDto fields beginner, advanced, diet, and
rehab to add `@Digits`(integer = 3, fraction = 2) alongside their existing
validation annotations, ensuring values match the Exercise.syncThreshold*
columns’ two-decimal scale.

Source: Path instructions

) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ public class Exercise {
@Column(precision = 5, scale = 2)
private BigDecimal syncThresholdAdvanced = new BigDecimal("85.00");

@Builder.Default
@Column(precision = 5, scale = 2)
private BigDecimal syncThresholdDiet = new BigDecimal("70.00");

@Builder.Default
@Column(precision = 5, scale = 2)
private BigDecimal syncThresholdRehab = new BigDecimal("50.00");

@Builder.Default
@Column(nullable = false)
private Integer expectedDurationMinutes = 15; // 예상 운동시간 (기본값: 15분)
Expand Down
Loading
Loading