Skip to content

feat: #29 진행 기록 저장 및 speed_factor 재계산 구현 - #30

Open
wngjs8114 wants to merge 3 commits into
devfrom
feature/#29-record-progress
Open

feat: #29 진행 기록 저장 및 speed_factor 재계산 구현#30
wngjs8114 wants to merge 3 commits into
devfrom
feature/#29-record-progress

Conversation

@wngjs8114

Copy link
Copy Markdown
Collaborator

관련 이슈

Closes #29

작업 내용

DailyPlanItem에 대한 학습 진행 결과를 저장하고, DailyPlanItem/DailyPlan 상태 및 speed_factor를 갱신

구조

  • speed_calibrator.py: calculate_speed_factor() 순수 함수 + recalculate_speed_factor() (exam에 저장)
  • progress_recorder.py: record_progress() (통합 저장), 검증 함수 2개, determine_daily_plan_status() 순수 함수

핵심 정책

  • speed_factor는 누적 갱신하지 않고, 해당 과목의 유효 ProgressLog 전체를 매번 재계산 (로그 수정 시 이중 반영 방지)
  • not_done 기록은 speed_factor 계산에서 제외
  • 대표 예상시간은 (min+max)/2 사용 (배치 기준인 max와는 별도, 과대평가 방지)
  • ProgressLog는 daily_plan_item당 하나, update_or_create로 재제출 처리
  • completion_percent 검증: done=100 고정, not_done=0 고정, partial=1~99 필수

이번 PR 범위 밖

  • finalize_daily_plan() (하루 마감, 복구 필요 여부 판단) — 다음 PR
  • recovery.py 연결 — 다음 PR

테스트

  • python manage.py test planner → 42개 통과

@wngjs8114
wngjs8114 requested a review from 6ye0m July 29, 2026 10:45
@6ye0m

6ye0m commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator
  1. speed_factor 계산 기준이 팀 결정(feat: #7 core/choices.py에 planner용 Enum 추가 #8)과 다름

python

speed_calibrator.py 현재 구현

base_expected_minutes = (task.estimated_min_minutes + task.estimated_max_minutes) / 2

팀 결정은 "속도 계산 기준은 DailyPlanItem.planned_minutes"인데, 실제로는 StudyTask.estimated_min/max_minutes의 평균을 씁니다. planned_minutes는 배치 시점 값이 스냅샷으로 고정돼 있어서(#28에서 estimated_max_minutes 그대로 저장) 나중에 StudyTask 예상시간이 바뀌어도 과거 기록이 안 흔들리는 장점이 있습니다. → log.daily_plan_item.planned_minutes 기준으로 교체 필요.

  1. partial(일부완료) 포함 여부가 "입력 최소화" 결정(fix: id 대신 email 사용 #9)과 충돌 가능
    현재 _is_valid_log()는 not_done만 제외하고 partial은 포함시켜 계산합니다. 팀이 "입력 최소화"를 택했다면 partial도 제외해야 함 — 이 부분 확정 여부 확인 필요.

🟠 코드 자체 버그/설계 결함

  1. recorded_at이 auto_now=True라 EMA 정렬 순서가 왜곡됨
    로그를 재제출(수정)하면 recorded_at이 "지금"으로 갱신되어, 실제로는 옛날 기록인데 EMA 계산에서 가장 최근 기록처럼 취급됩니다. "학습이 실제로 있었던 날짜"(daily_plan_item.daily_plan.date) 기준으로 정렬하거나, recorded_at을 auto_now_add로 바꾸는 걸 검토해야 합니다.

  2. 이상값 클램핑이 루프 밖에서만 적용됨

python
factor = factor * (1 - BLEND_WEIGHT) + ratio * BLEND_WEIGHT # 매번 미클램프
...
return round(_clamp(factor, MIN, MAX), 3) # 최종에만 클램프

극단값 하나가 중간 계산 전체를 왜곡시킬 수 있음. 매 blend 단계마다 클램프하도록 수정 추천. 실제 계산 예시로 검산했을 때 결과가 2.0 vs 1.7로 갈렸음(클램프 위치에 따라).

@wngjs8114

Copy link
Copy Markdown
Collaborator Author

피드백 반영했습니다:

  1. speed_factor 계산 기준을 StudyTask.estimated_min/max_minutes 평균 대신 DailyPlanItem.planned_minutes(배치 당시 스냅샷)로 변경했습니다. 말씀하신 대로 이렇게 해야 StudyTask 예상시간이 나중에 재계산돼도 과거 진행 기록의 속도 계산이 흔들리지 않습니다.

  2. partial(일부완료) 제외 여부는 확인이 필요할 것 같아요 — "입력 최소화" 결정을 어느 PR/논의에서 확정하신 건지 알려주실 수 있을까요? PR core/choices.py에 planner용 Enum 추가 #7(core/choices Enum 추가), fix: id 대신 email 사용 #9(id→email)에서는 관련 내용을 못 찾아서요. 실제 결정 근거를 알려주시면 그에 맞게 반영하겠습니다.

  3. recorded_at(auto_now) 정렬 왜곡 문제 확인했습니다 — recorded_at 대신 daily_plan_item.daily_plan.date(실제 공부한 날짜)로 정렬하도록 변경했습니다. 마이그레이션 없이 정렬 기준만 바꿔서 해결했습니다.

  4. 클램핑을 매 blend 단계마다 적용하도록 수정했습니다. 극단값이 중간 계산을 왜곡하지 않습니다.

전체 42개 테스트 통과 확인했습니다. 확인 부탁드려요!

@wngjs8114

Copy link
Copy Markdown
Collaborator Author

일부완료 정책은 actual_minutes + completion_percent를 입력받고,
speed_factor 계산에도 포함하는 것으로 확정했습니다.

일부완료 시 완료한 분량의 기준 예상시간은

DailyPlanItem.planned_minutes × completion_percent / 100

으로 계산하고, 실제 공부시간을 해당 값으로 나눈 비율을
speed_factor 보정에 반영하면 됩니다.

따라서 현재처럼 partial을 유효 로그에 포함하는 방향으로 유지하면 됩니다.

@6ye0m

6ye0m commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

MIN_SPEED_FACTOR = 0.5
MAX_SPEED_FACTOR = 2.0

clamp 값 0.7~1.5 로 수정하고 머지하면 될 듯 합니다!

@wngjs8114

Copy link
Copy Markdown
Collaborator Author

확인 감사합니다. clamp 범위를 0.7~1.5로 조정했습니다.

추가로, 같은 날짜에 ProgressLog가 여러 개일 때 EMA 계산 순서가
daily_plan_item__order 기준으로 결정적으로 처리되도록
recalculate_speed_factor()의 정렬 기준에 타이브레이커
(daily_plan_item__order, pk)를 추가했습니다.

관련 테스트 3개(하한선 clamp, 동일 날짜 정렬 순서 검증) 추가했고,
전체 44개 테스트 통과 확인했습니다.

@wngjs8114

Copy link
Copy Markdown
Collaborator Author

추가 리뷰 및 일부 완료 정책까지 최종 반영했습니다.

  1. speed_factor 계산 기준

기존의 StudyTask.estimated_min_minutes와
estimated_max_minutes 평균 대신,

DailyPlanItem.planned_minutes를 배치 당시 예상시간 스냅샷으로
사용하도록 변경했습니다.

따라서 StudyTask 예상시간이 이후 다시 계산되더라도,
과거 진행 기록의 속도 계산 기준은 변경되지 않습니다.

  1. 상태별 speed_factor 반영 정책

다음 상태는 speed_factor 계산에 포함합니다.

  • COMPLETED
  • PARTIAL

다음 상태는 제외합니다.

  • NOT_DONE

NOT_DONE은 실제 학습이 수행되지 않아 학습 속도를 판단할 수 없기
때문입니다.

  1. 일부 완료 계산 정책

일부 완료 시 사용자에게 다음 값을 입력받는 것으로 확정했습니다.

  • 실제 공부시간
  • 완료 진행률

완료한 분량의 기준 예상시간은 다음과 같이 계산합니다.

completed_expected_minutes
= DailyPlanItem.planned_minutes × completion_percent / 100

관측 속도 비율은 다음과 같이 계산합니다.

observed_ratio
= actual_minutes / completed_expected_minutes

따라서 PARTIAL도 현재 구현처럼 유효 로그에 포함합니다.

  1. 로그 정렬

recorded_at은 수정 시점에 따라 순서가 달라질 수 있으므로,
실제 학습일인 DailyPlan.date를 기준으로 정렬하도록 변경했습니다.

같은 날짜에 여러 로그가 존재할 때도 EMA 결과가 결정적으로 유지되도록
다음 타이브레이커를 추가했습니다.

DailyPlan.date
→ DailyPlanItem.order
→ ProgressLog.pk

  1. clamp 처리

speed_factor clamp 범위를 0.7~1.5로 조정했고,
최종 결과에만 적용하지 않고 각 EMA blend 단계마다 적용하도록
수정했습니다.

관련 테스트를 추가했으며 planner 테스트 44개 전체 통과 확인했습니다.
재검토 부탁드립니다.

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.

[planner] 진행 기록 저장 및 speed_factor 재계산 구현

2 participants