이전/다음 두 버튼만 유지. radio key 고정으로 stage 변경 후
session_state가 stale → 적용 버튼이 의도치 않게 뜨던 문제 제거.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- start.sh: 호스트 LAN IP 자동 감지 후 PUBLIC_BASE_URL 세팅, 이제 QR이 172.x 컨테이너 IP 대신 실제 LAN IP를 가리킴
- docker-compose.yml: PUBLIC_BASE_URL 환경변수 pass-through 추가
- app.py: topics min-height 480→360, font-size/line-height 상향, vote counter를 QR 위로 이동, pct 계산 단순화
- README: 실행 섹션 교체 (start.sh 권장, raw/최소 방식 병기)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- show-urls.sh: localhost + LAN IP 포함 모든 URL 출력 (참가자/어드민/시상식)
- admin 페이지에 다른 페이지 URL expander 추가 (ceremony 링크 클릭 가능)
- README에 사용법 추가
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DB(sqlite + WAL) 제거. 모든 state를 단일 JSON 파일로 통합.
일회용/내부용이라 유지보수성/확장성보다 단순성 우선.
변경:
- app.py: sqlite3 import 제거. load_data/save_data + threading.RLock + atomic write
- votes: list of dict
- titles, tie_breaks, settings: dict
- people: roster (assign_teams가 채움)
- 누락 키 자동 보강
- assign_teams.py: hackathon.json 단일 출력. 기존 votes/titles 보존
- Dockerfile/compose: votes.db volume 제거. hackathon.json read-write mount
- tests/e2e.py: 12개 (12/12 통과). load/save/insert_vote/clear_votes/atomic 추가
- README: 새 데이터 구조 문서화
- roster.json/participants.json 제거 (hackathon.json으로 통합)
호스트 편집 워크플로:
- jq/vi로 hackathon.json 직접 편집
- 앱 매 요청 reload — 컨테이너 재시작 불필요
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
호스트에서 직접 편집 가능한 단일 JSON으로 명단 일원화.
앱이 매 요청마다 디스크에서 reload → 컨테이너 재시작 불필요.
변경:
- roster.json 새 형식: {"people": [{"name", "team", "dept", "senior", "notes"}, ...]}
- assign_teams.py: roster.json + legacy participants.json 둘 다 출력
- app.py: get_participants() / get_teams() 매 호출 reload
- PARTS = get_participants() / TEAMS = get_teams() 함수 안에서 호출
- 모듈 레벨 PARTICIPANTS/TEAMS 제거
- load_roster() roster.json 우선, 없으면 legacy fallback
- docker-compose: roster.json + participants.json 둘 다 mount
- Dockerfile: ROSTER env + roster.json COPY
사용자 워크플로:
- 사람 다른 팀 옮기기: roster.json에서 그 사람 'team' 값만 변경
- 자동 배정 재실행: python3 assign_teams.py
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#14 이미 투표한 사람 UX
- selectbox에 ✅ 마크
- 선택 시 친절한 에러 (재투표 불가 안내, 진행자 문의 가이드)
#15 SQLite WAL 모드
- get_conn에서 PRAGMA journal_mode = WAL
- synchronous = NORMAL (성능 + 안전 균형)
- 동시 read/write 충돌 방지 (35명 동시 제출 안전)
- timeout 10초 (busy 시 retry)
#17 시상 결과 archive
- ceremony 진입 시 1회 winners를 results_<timestamp>.json 저장
- DB 손실 보험. 위치: /data 볼륨
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1. 투표 마감 락 (settings 테이블 + 어드민 토글)
- 시상 도중 결과 바뀜 방지
- ceremony 진입 시 voting_open이면 경고 + 차단
2. 빈 결과 ceremony 차단
- 투표 0건이면 진입 불가
3. 타임존 KST (Dockerfile tzdata + TZ=Asia/Seoul)
- 감사 로그 시각 정확
4. CSV UTF-8 BOM
- Excel에서 한글 정상 표시
5. 사번 입력 안내 강화
- placeholder + help: 민감정보 입력 금지
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- DB tie_breaks 테이블 (category, winner_team, method, decided_at)
- compute_winners()가 동률 시 status='tie' 반환, tied 후보 표시
- 어드민: 동률 부문에 🎲 추첨 버튼 + 수동 선택 라디오 + 결정 취소
- 우승 표시에 결정 방식 태그 (🎲 추첨 / 🖊️ 수동)
- ceremony: 동률 미해결 발견 시 진입 차단, 어드민 처리 유도
흐름:
1. 어드민에서 동률 알림 확인
2. 즉석 추첨 또는 수동 선택으로 결정
3. ceremony 진입하면 정상 reveal
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 투표 폼에 사번 입력 필수 추가
- DB votes 테이블에 employee_id 컬럼 (마이그레이션 자동)
- 어드민 감사 로그 expander: 시각/이름/사번/본인팀/투표내역 표
- CSV 내려받기 버튼
- 같은 사번이 여러 이름으로 투표 시 자동 의심 마크
- 안내 expander에 사번 입력/추적 설명 추가
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
수상 결정 순서:
1. 실용성상(팜레스트, 최고가) → 1위 결정
2. 완성도상(양우산) → 1번 수상자 제외 후 1위
3. 재미상(손선풍기) → 1, 2번 수상자 제외 후 1위
발표(reveal) 순서는 그대로 손선풍기 → 양우산 → 팜레스트 (긴장감).
compute_winners() 헬퍼로 admin/ceremony 둘 다 동일 로직.
admin 분포 expander에 '상위상수상으로 제외' 마커.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 단계 진행:
1. 시상식 시작 화면 → 시작 버튼
2. 부문별 announce (label + 상품) → 🥁🥁🥁 → 우승팀 공개 버튼
3. 우승팀 reveal: gradient gold 큰 폰트 + balloons + fadeIn 애니메이션
4. 다음 부문 → 반복 → 모든 시상 완료 (snow + balloons)
진행자 클릭만으로 진행. session_state로 단계 관리.
CATEGORIES에 상품 매핑 (재미상=손선풍기, 완성도상=팜레스트, 실용성상=양우산).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- DB 테이블 team_titles 추가
- 어드민 페이지에 팀별 제목 입력 폼 (저장 즉시 반영)
- 투표 라디오 옵션이 '팀1 — 결과물 제목' 형식으로 표시
- 우승 발표/시상 텍스트에도 제목 포함
- 제목 미입력 시 팀명만 (fallback)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- assign_teams.py: 부서 다양성 제약(같은 부서 ≤2명) 시드 고정 배정
- participants.json: 이름→팀 매핑 산출물
- app.py: 이름 선택 → 본인 팀 자동 표시 (수동 입력 부정 차단)
- 어드민 참여율 메트릭 + 미투표자 목록
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 35명/7팀/3분야(재미·완성도·실용성) 투표
- 본인 팀 제외 자동 처리
- 이름 UNIQUE 중복 방지
- 진행자 어드민 페이지: 1위와 2위 차이만 공개, 하위 팀 표수는 비공개
- sqlite 단일 파일 저장
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>