feat: 우선순위 기반 1팀 1상 자동 적용
수상 결정 순서: 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>
This commit is contained in:
84
app.py
84
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
|
||||
|
||||
Reference in New Issue
Block a user