Commit Graph

13 Commits

Author SHA1 Message Date
Hyungi Ahn 9094b2dbc5 feat(study): Phase 1-E 풀이 선별 알고리즘 — bucket + stage 비율
vision 의 단일 풀이 진입점 — stage (intro/learning/pre_exam) + size 옵션으로
같은 endpoint 가 다른 분포의 문제 출제.

services/study/quiz_selection.py:
- bucket: unattempted / wrong_or_unsure / due_review / regressed / frequent / random
- stage 별 비율:
  - intro:    unattempted 55, wrong_or_unsure 30, frequent 15
  - learning: due_review 20, wrong_or_unsure 40, unattempted 30, frequent 10
  - pre_exam: due_review 20, wrong_or_unsure 30, regressed 10, frequent 20, random 20
- bucket 우선순위 (dict 순서) — 다음 bucket 은 이미 뽑힌 qid 제외
- 후보 부족 시 random backfill, 그래도 부족 시 ValueError

api/study_topics.py:
- QuizSessionStartRequest 에 stage / size 옵션 추가
- stage 명시 시 select_questions_for_quiz 사용
- stage 미명시 시 기존 PR-12-B 경로 (subject bucket + spacing) 호환 유지

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 09:30:11 +09:00
Hyungi Ahn fc8aea1649 feat(study): 반복 출제 라벨 등급 + cosine 임계값 0.85 조정
- round_count 별 등급 매핑 (단골/잘 나오는 반복 출제/반복 출제/신출/빈출)
  - ≥7 단골, 5–6 잘 나오는 반복 출제, 3–4 반복 출제,
    2 + max(연도)≥2024 신출, 2 + 모두 옛 빈출
- SIMILAR_THRESHOLD 0.88 → 0.85 (5-source 분포 측정 결과 자연 갭 위치 반영)
- API 응답 + 프론트 3곳 (보기/통합뷰/결과 카드) 라벨 일괄 통일

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 08:50:39 +09:00
Hyungi Ahn 13404cd366 feat(study): 같은 유형 과밀 방지 — 출제 단계 spacing (PR-12-B)
학습 의미: 한 quiz 세션 안에서 같은 유형 문제가 과도하게 몰리지 않게 분산.
같은 유형을 없애는 게 아니라 펼치는 것 — dedup/제거 프레임 금지.

- 마이그레이션 210: study_quiz_sessions.quiz_mode VARCHAR(30) DEFAULT 'random'
- ORM: StudyQuizSession.quiz_mode 필드
- service.related_types: apply_type_spacing helper 추가
  - SPACING_THRESHOLD=0.88 (회차 무관 — PR-12-A 회차 필터 재사용 X)
  - PER_TYPE_CAP=2 (local neighbor cap, transitive cluster 보장 X)
  - SPACING_BUFFER_RATIO=2.0
  - 3단계 fallback: ready spacing → pending 보충 → hold cap 위반 fallback
  - debug 로그 type_spacing_applied subject=... ready=N selected=M ...
- _select_questions_for_topic: subject bucket 단위 spacing (과목 균등 보호)
- QuizMode Enum (random) — 향후 frequent_focus/wrong_variants 예약
- start_quiz_session 에 quiz_mode 받기 + apply_spacing 전달
- 프론트 startNewQuiz body 에 quiz_mode='random' 명시

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:45:15 +09:00
Hyungi Ahn cbe852bb37 feat(study): 반복 출제 / 유사 유형 분리 표시 (PR-12-A)
학습 의미가 회차 간 반복성 — 차단/제거가 아니라 패턴 표시 frame.

- 신규 service `related_types.py` — threshold/회차 필터/round_count 계산 공유
  - REPEAT >= 0.95 / SIMILAR 0.88~0.95
  - 회차 조건 백엔드 강제 (자기 자신/같은 회차/null exam_round candidate 제외)
  - round_count: related_count == 0 → 0 (현재 회차만 1로 채우지 않음)
- GET /study-questions/{qid}/related-types — 단건 분류 (repeat_questions / similar_questions)
- POST /study-topics/{tid}/related-types-bulk — 카드 배지용 카운트 batch
  - 비교 대상 = 토픽 전체 ready pool (입력 qid 끼리 비교 X)
  - 응답 키 보존 — 권한 없음/임베딩 미준비 등도 (0,0,0,0)
- 보기 페이지: PR-11 비슷한 문제 토글 제거 + 🔥 반복 출제 / 🧩 유사 유형 두 섹션 자동 노출
  - 헤더 = round_count "N개 회차", 본문 위 = related_count "관련 N문제"
  - source_status / source_exam_round 안내 분기
- 결과 페이지 (틀린/모르겠음 카드): bulk 호출 후 round_count >= 2 일 때만 배지
- 통합뷰 회차 expand 시 lazy bulk 호출 — 같은 회차 캐시
- 기존 /similar 엔드포인트 유지 (raw 디버깅용)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:09:14 +09:00
Hyungi Ahn 1cf64fd11e feat(study): 문제 회차별 그룹 + 읽기전용 보기 페이지 (PR-11)
- 통합뷰 문제 섹션: 평면 리스트 → 회차별 아코디언 (디폴트 모두 접힘)
- 회차 정렬: "YYYY년 N회" 파싱 → year desc / round desc (localeCompare 단독 회귀 차단)
- 회차 행 라벨: "총 시도 N건 · 마지막 결과: 정답 K / 오답 M" (누적/마지막 혼동 회피)
- 회차 미지정 그룹은 노란 톤 + 안내, 표시 문자열은 UI 전용 (원본 NULL 분리)
- 본문 / [편집] 링크 구조 분리로 이벤트 버블링 충돌 차단
- /study/topics/{tid}/questions/{qid} 신규 — KaTeX 마크다운 렌더 + 정답 표시 +
  AI 해설 5상태 (idle/loading/success/stale/error) + 비슷한 문제 + prev/next
- prev/next URL 직접 접근 — 단건 fetch + 같은 회차 목록 fetch 자체 처리
- page_size=200 만땅 + total>200 시 토스트 안내 (조용히 자르지 않음)
- 사용자 입력 해설/이미지 없으면 섹션 숨김, exam_round NULL 이면 prev/next 비활성
- StudyTopicQuestionSummary 에 exam_question_number 추가 (회차 안 정렬 키)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 07:01:27 +09:00
Hyungi Ahn 6e25523600 fix(study): quiz_session 결과 — StudyQuestionImage.position → sort_order 재사용
PR-10 결과 페이지에서 GET /quiz-sessions/{sid} 가 500. 이미지 batch 호출에서
존재하지 않는 컬럼 position 사용 → AttributeError. 기존
_images_for_questions_batch 헬퍼 (sort_order 기준 + served_url 포함) 재사용.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 16:52:52 +09:00
Hyungi Ahn 7f4d64c6df feat(study): 문제풀이 세션 + 결과 카드 + 학습완료 체크 (PR-10)
- study_quiz_sessions 테이블 (한 토픽 in_progress 1개 partial unique)
- study_question_attempts 에 quiz_session_id + reviewed_at 컬럼
- 풀이 진행률 서버 단일 진실 (cursor) — 나갔다 와도 이어풀기 가능
- 통합뷰: 진행 중 카드(이어풀기) + 최근 완료 결과 카드(미확인 N건 배지)
- 신규 /quiz-sessions/[sid] 결과 페이지 (3 카테고리 + AI 해설 + 분야 설명 + 학습완료 토글)
- /review 페이지는 풀이만, 마지막 문제 풀이 후 결과 페이지로 redirect
- 마이그레이션 206~209 (single-statement, asyncpg 호환)
- API: POST/GET/PATCH /study-topics/{tid}/quiz-sessions(/{sid}),
       PATCH /study-question-attempts/{aid}/review-mark
- AttemptCreate.quiz_session_id 추가 — submit_attempt 가 같은 트랜잭션에서
  세션 cursor + count 증가, 마지막이면 status='done' + finished_at

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 16:49:21 +09:00
Hyungi Ahn d968b2d901 feat(study): 문제풀이 모드 개편 + 결과 분류 + 분야 설명 (PR-9)
- 라벨 "복습 시작" → "문제풀이"
- attempts.outcome 컬럼 + selected_choice nullable (correct/wrong/unsure)
- 풀이 중 정답·해설·AI·비슷한 문제 모두 비노출, 답 클릭 시 자동 진행
- "모르겠음" 5번째 옵션 추가
- 결과 화면 = 정답/틀린/모르겠음 3 카테고리 탭, 카드 클릭 expand
  - 틀린 → PR-3 AI 해설 (RAG)
  - 모르겠음 → 분야(subject+scope) 설명 AI 즉석 생성 + 캐시 (PR-9 신규)
- 분야 설명 RAG: 매핑 documents 청크 + 같은 분야 다른 문제·해설 → bge-reranker
- 마이그레이션 200~205 (single-statement, asyncpg 호환)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 15:58:35 +09:00
Hyungi Ahn 8803e6a0fd feat(study): 시험·회차·문항 관리 (PR-6)
기사시험 회차별 100문제 채워가기 시나리오. 문제 입력 페이지를 단순 폼에서
"회차 진행률 추적·재개" 도구로 보강.

데이터 모델 (migrations 195~197):
- study_topics: exam_round_size INT CHECK 1~300 (회차당 문항 수, NULL=미설정)
  + exam_subjects JSONB DEFAULT '[]' (과목 리스트, 입력 페이지 드롭다운 옵션)
- study_questions: exam_question_number SMALLINT CHECK >0 (회차 안 문항 번호)
- partial idx (study_topic_id, exam_round, exam_question_number) WHERE
  deleted_at IS NULL AND exam_round IS NOT NULL — 회차별 max+count 고속화

백엔드:
- POST /questions: exam_round 명시 + exam_question_number 미명시 시 서버가
  같은 토픽·회차의 max+1 자동 채움
- 신규 GET /api/study-topics/{id}/exam-rounds: 회차별 진행률 집계
  {exam_round_size, items: [{exam_round, question_count, max_question_number,
   next_question_number, is_complete}]}
- StudyTopic Create/Update/Response/Meta 에 exam_round_size·exam_subjects
- StudyQuestion Create/Update/Response 에 exam_question_number
- exam_question_number 변경은 embedding stale 트리거에서 제외 (의미 영향 없음)

프론트:
- 토픽 생성/편집 모달: "시험 정보" 섹션 (회차당 문항 수 + 과목 리스트
  +추가/제거 칩)
- /study/topics/[id]/exam-rounds 신규 페이지: 회차 카드 + 진행 바 +
  [N번부터 이어서] 버튼 + [새 회차 시작] 모달
- 통합뷰 문제 섹션 헤더에 [회차 보기] 진입점
- /questions/new 페이지 전면 개편:
  - 시험명 = topic.name 자동 prefill
  - 과목 드롭다운 (topic.exam_subjects + 기존 distinct, "직접 입력" 토글)
  - 회차 드롭다운 (기존 distinct + "새 회차")
  - 문항 번호 자동 (회차 선택 시 next_question_number, 새 회차 = 1)
  - 진행률 바 (현재/exam_round_size)
  - 출처/메모 자동 합성 "회차 N번" (수정 가능)
  - "저장 후 계속 입력" → 본문/보기/정답 reset, 회차 유지, 문항 +1
  - 회차 변경 감지 시 문항 번호 1로 reset
  - exam_round_size 도달 시 회차 강조 + "저장 후 계속 입력" 비활성
- query string ?exam_round=&start_qnum= 지원 (회차 목록에서 재개 진입)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 09:31:06 +09:00
Hyungi Ahn 0e2a430a6c fix(study): 통합뷰 자료 섹션 카테고리 트리 그룹핑 + 접기
가스기사처럼 한 워크스페이스에 273건 자료가 묶이면 평면 리스트로 쭉 나열
되어 통합뷰가 무너졌음. /study/topics/[id] 자료 섹션을 자료실 카테고리
경로 기반 트리로 그룹핑하고 노드별 접기/펼치기 도입. 기본값 모두 접힘.

백엔드: StudyTopicDocumentSummary 에 library_paths(`@library/<path>` 태그
에서 prefix 제거) 필드 추가. 그룹핑은 첫 path 만 사용 (단순화).

프론트: documents 를 path segment 별로 트리 빌드 → snippet 재귀 렌더링.
헤더에 "자료 N개 · 카테고리 K개 · [모두 펼치기/접기]" 컨트롤. 분류 없는
자료는 "분류 없음" 그룹으로 별도. 자료 0건 path 는 자동 누락.

필기/문제 섹션은 분류축이 달라(certification/subject vs subject) 동일
트리 못 쓰므로 본 PR 범위 밖. 후속에서 패턴 일관성 검토.
2026-04-28 08:14:58 +09:00
Hyungi Ahn 4b7156061e feat(study): 문제은행 + 복습모드 (study_questions)
study_topic 워크스페이스에 4지선다 문제은행 자산 트랙 추가. 기사시험 필기
대비 시나리오 — 빠른 반복 입력 + 과목별 균등 추출 복습 + 정오답 누적.

데이터 모델 (migrations 186~190):
- study_questions: study_topic 1:N, soft delete, is_active 토글, correct_choice
  SMALLINT CHECK 1~4
- study_question_attempts: 답 제출 1행 누적. study_question_id FK는 ON DELETE
  RESTRICT (이력 보존 원칙 — hard delete 실수로 풀이 기록 소실 차단)

설계 원칙:
- 문제 삭제는 API 에서 soft delete only. attempts FK RESTRICT 로 DB 레벨도 보호
- correct_choice 변경 시 기존 attempts.is_correct 재계산 안 함 (시점 사실 보존)
- 복습 default = 과목별 target_per_subject(20) 무작위 균등 추출. 한 과목이
  부족하면 가용한 만큼만
- wrong_only=true 정의 = 가장 최근 attempt 가 오답인 문제 (latest-wrong, ever-wrong 아님)
- 출제 응답에서 정답·해설 비공개. 답 제출 시점에만 노출
- subject/scope 강한 enum 미사용 (자유 텍스트, 자동완성은 후속)

API: /api/study-topics/{id}/questions, /review/questions, /api/study-questions/{id},
/attempt. 통합뷰(/study-topics/{id}) 응답에 sections.questions / stats.question_count
추가. 기존 question_set_count 는 후속 PR(회차/모의고사 묶음)용으로 보존.

프론트: /study/topics/[id]에 문제 섹션 + "새 문제"/"복습 시작" 진입.
/questions/new (저장 후 계속 입력 + sessionStorage persistent),
/questions/[qid]/edit (정답 변경 시 attempts 재계산 안 됨 안내 배너),
/review (시작 옵션 → 풀이 → 마지막 요약).

후속 PR 예정: 오답노트/취약 과목 리포트, AI 해설/클러스터링, spaced
repetition, 이미지 OCR 입력, CSV import, study_question_sets 묶음.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 08:00:37 +09:00
Hyungi Ahn 62afc571c0 feat(study): 카테고리 트리에서 자료 일괄 추가
자료 추가 모달이 1건씩 검색·체크박스만 지원해서 같은 카테고리에 자료가
많을 때 비효율적. /api/library/tree 의 카테고리 구조를 모달 좌측에 띄우고,
노드 옆 아이콘 한 번으로 그 path 하위 자료 전체를 한 번에 매핑.

백엔드: POST /api/study-topics/{id}/documents/by-path 추가. user_tags
@library/<path> prefix 매칭(documents.py 의 list_library_documents 와
동일한 EXISTS 쿼리)으로 100건 limit 우회. 응답은 linked_count /
skipped_existing_count / total_in_path 카운트만 노출.

프론트: 모달을 max-w-4xl + grid(트리/자료) 레이아웃으로 개편. 트리 노드
클릭 = 우측 자료 목록 path 필터링, 노드 옆 FolderPlus 버튼 = 즉시 일괄
추가. 검색·체크박스·전체선택은 그대로. 모바일은 트리가 상단 max-h-40vh
영역으로 stack.
2026-04-28 07:29:59 +09:00
Hyungi Ahn 63ed4d81e5 feat(study): study_topics 학습 워크스페이스 컨테이너 도입
필기 세션과 자료(library document)를 한 학습 주제(예: 가스기사) 아래로 묶는
1차 컨테이너. 향후 단어장/오디오/문제세트 등 학습 자산이 같은 묶음으로 들어올 수
있도록 응답 구조(sections + stats)를 dict 기반으로 설계.

데이터 모델 (migrations 179~185):
- study_topics: user_id × name partial unique (active 행만), soft delete
- study_sessions.study_topic_id: 1:N nullable FK (ON DELETE SET NULL)
- study_topic_documents: 자료 N:M 매핑 (user_id 반정규화로 권한 격리)

설계 원칙:
- documents.category(자료실 UI 축)와 직교 → 자료실 facet/카테고리 미터치
- StudySession.certification/subject/topic 보존 (세부 메타로 계속 사용)
- study_type은 느슨한 분류 (강한 enum 미사용, jlpt_n3 등 확장 여지)
- polymorphic study_topic_items 영구 금지 → 자산 타입별 조인 테이블 추가 방식

API: /api/study-topics CRUD + /by-document/{id} + 자료/세션 매핑 엔드포인트.
프론트: /study/topics 목록 + /study/topics/[id] 통합 뷰(필기·자료 두 트랙) +
        write 폼에 워크스페이스 드롭다운 + study hub 진입 카드.

후속 PR-2 어학 UX, PR-3 오디오 자산, PR-4 AI retrieval scope.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 07:06:37 +09:00