From 1c55b77bc1a478b931223eb5fb42ec376adfe916 Mon Sep 17 00:00:00 2001 From: th-kim0823 Date: Sat, 25 Apr 2026 20:00:56 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9A=B0=EC=84=A0=EC=88=9C=EC=9C=84=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=201=ED=8C=80=201=EC=83=81=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 수상 결정 순서: 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) --- app.py | 84 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/app.py b/app.py index f284aed..458a8e4 100644 --- a/app.py +++ b/app.py @@ -37,6 +37,10 @@ CATEGORIES = [ ("utility_team", "🛠 실용성상", "팜레스트 5개"), ] +# 수상 결정 우선순위 (높을수록 먼저 결정, 후순위 상에서 그 팀 제외) +# 팜레스트(실용성) > 양우산(완성도) > 손선풍기(재미) +PRIZE_PRIORITY = ["utility_team", "polish_team", "fun_team"] + def get_conn(): conn = sqlite3.connect(DB_PATH) @@ -90,6 +94,36 @@ def fmt_team(team, titles): return f"{team} — {t}" if t else team +def compute_winners(): + """ + 우선순위 기반 1팀 1상 보장. + PRIZE_PRIORITY 순으로 결정, 이미 수상한 팀은 후순위 상에서 제외. + return: dict[col] = (winner_team, winner_votes, diff_with_2nd, all_rows_excluded) + """ + conn = get_conn() + rankings = {} + for col, _, _ in CATEGORIES: + rankings[col] = conn.execute( + f"SELECT {col} AS team, COUNT(*) AS c FROM votes " + f"GROUP BY {col} ORDER BY c DESC, team ASC" + ).fetchall() + conn.close() + + winners = {} + excluded = set() + for col in PRIZE_PRIORITY: + rows = rankings[col] + filtered = [(t, c) for t, c in rows if t not in excluded] + if not filtered: + winners[col] = None + continue + winner, votes = filtered[0] + runner = filtered[1][1] if len(filtered) > 1 else 0 + winners[col] = (winner, votes, votes - runner, filtered) + excluded.add(winner) + return winners, rankings + + def render_voter(): st.title("🗳 해커톤 투표") st.caption("이름 선택 → 본인 팀 자동 매핑 → 본인 팀 제외 3분야 투표. 한 번만 제출 가능.") @@ -204,24 +238,29 @@ def render_admin(): st.rerun() st.divider() - st.subheader("📊 분야별 집계") + st.subheader("📊 분야별 집계 (우선순위 적용 — 1팀 1상)") + st.caption( + "수상 결정 순서: 팜레스트(실용성) → 양우산(완성도) → 손선풍기(재미). " + "이미 받은 팀은 후순위 상에서 제외." + ) - public_lines = [] # 시상식 발표용 (하위 비공개) + winners, rankings = compute_winners() + awarded_teams = {w[0] for w in winners.values() if w} + public_lines = [] - for col, label, _ in CATEGORIES: - rows = conn.execute( - f"SELECT {col} AS team, COUNT(*) AS c FROM votes GROUP BY {col} ORDER BY c DESC, team ASC" - ).fetchall() - - st.markdown(f"### {label}") + for col, label, prize in CATEGORIES: + rows = rankings[col] + st.markdown(f"### {label} ({prize})") if not rows: st.caption("표 없음") continue - winner_team, winner_votes = rows[0] - runner_votes = rows[1][1] if len(rows) > 1 else 0 - diff = winner_votes - runner_votes + result = winners.get(col) + if not result: + st.warning("후보 없음 (모두 우선순위 상 수상)") + continue + winner_team, winner_votes, diff, _ = result winner_label = fmt_team(winner_team, titles) st.success( f"**우승: {winner_label}** — {winner_votes}표 (2위와 {diff}표 차이)" @@ -230,9 +269,12 @@ def render_admin(): f"- {label} 우승: **{winner_label}** ({winner_votes}표, 2위와 {diff}표 차이)" ) - with st.expander("전체 분포 (진행자만)"): + with st.expander("전체 분포 — raw (제외 적용 전)"): for team, c in rows: - st.write(f"- {fmt_team(team, titles)}: {c}표") + marker = "" + if team in awarded_teams and team != winner_team: + marker = " 🚫상위상수상으로 제외" + st.write(f"- {fmt_team(team, titles)}: {c}표{marker}") st.divider() st.subheader("🎤 시상식 발표용 (복사해서 화면 공유)") @@ -256,19 +298,15 @@ def render_ceremony(): return titles = get_titles() - conn = get_conn() + winners, _ = compute_winners() + # CATEGORIES 순서로 reveal (손선풍기 → 양우산 → 팜레스트) results = [] for col, label, prize in CATEGORIES: - rows = conn.execute( - f"SELECT {col} AS team, COUNT(*) AS c FROM votes " - f"GROUP BY {col} ORDER BY c DESC, team ASC" - ).fetchall() - if rows: - winner, votes = rows[0] - runner = rows[1][1] if len(rows) > 1 else 0 - results.append((label, prize, winner, votes, votes - runner)) - conn.close() + result = winners.get(col) + if result: + winner, votes, diff, _ = result + results.append((label, prize, winner, votes, diff)) if "ceremony_step" not in st.session_state: st.session_state.ceremony_step = 0