feat: 결과물 제목 라이브 입력 + 투표 라디오 표시
- DB 테이블 team_titles 추가 - 어드민 페이지에 팀별 제목 입력 폼 (저장 즉시 반영) - 투표 라디오 옵션이 '팀1 — 결과물 제목' 형식으로 표시 - 우승 발표/시상 텍스트에도 제목 포함 - 제목 미입력 시 팀명만 (fallback) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
70
app.py
70
app.py
@@ -53,10 +53,43 @@ def get_conn():
|
||||
)
|
||||
"""
|
||||
)
|
||||
conn.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS team_titles (
|
||||
team_name TEXT PRIMARY KEY,
|
||||
title TEXT NOT NULL DEFAULT ''
|
||||
)
|
||||
"""
|
||||
)
|
||||
conn.commit()
|
||||
return conn
|
||||
|
||||
|
||||
def get_titles():
|
||||
"""팀명 → 결과물 제목 dict. 매 호출 DB 조회 (라이브 반영)."""
|
||||
conn = get_conn()
|
||||
rows = conn.execute("SELECT team_name, title FROM team_titles").fetchall()
|
||||
conn.close()
|
||||
return dict(rows)
|
||||
|
||||
|
||||
def set_title(team, title):
|
||||
conn = get_conn()
|
||||
conn.execute(
|
||||
"INSERT INTO team_titles (team_name, title) VALUES (?, ?) "
|
||||
"ON CONFLICT(team_name) DO UPDATE SET title = excluded.title",
|
||||
(team, title.strip()),
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
def fmt_team(team, titles):
|
||||
"""팀 라벨 — 제목 있으면 'N팀 — 제목' 없으면 'N팀'."""
|
||||
t = titles.get(team, "")
|
||||
return f"{team} — {t}" if t else team
|
||||
|
||||
|
||||
def render_voter():
|
||||
st.title("🗳 해커톤 투표")
|
||||
st.caption("이름 선택 → 본인 팀 자동 매핑 → 본인 팀 제외 3분야 투표. 한 번만 제출 가능.")
|
||||
@@ -77,14 +110,21 @@ def render_voter():
|
||||
return
|
||||
|
||||
my_team = PARTICIPANTS[name]
|
||||
st.info(f"본인 팀: **{my_team}**")
|
||||
titles = get_titles()
|
||||
st.info(f"본인 팀: **{fmt_team(my_team, titles)}**")
|
||||
candidates = [t for t in TEAMS if t != my_team]
|
||||
|
||||
with st.form("vote", clear_on_submit=False):
|
||||
st.divider()
|
||||
picks = {}
|
||||
for col, label in CATEGORIES:
|
||||
picks[col] = st.radio(label, candidates, index=None, key=col)
|
||||
picks[col] = st.radio(
|
||||
label,
|
||||
candidates,
|
||||
index=None,
|
||||
key=col,
|
||||
format_func=lambda t: fmt_team(t, titles),
|
||||
)
|
||||
submitted = st.form_submit_button("제출")
|
||||
|
||||
if submitted:
|
||||
@@ -144,6 +184,25 @@ def render_admin():
|
||||
for n in sorted(not_voted):
|
||||
st.write(f"- {n} ({PARTICIPANTS[n]})")
|
||||
|
||||
st.divider()
|
||||
st.subheader("📝 팀별 결과물 제목 입력")
|
||||
st.caption("발표 직후 입력하면 투표 페이지에 즉시 반영됩니다.")
|
||||
titles = get_titles()
|
||||
with st.form("titles_form"):
|
||||
new_titles = {}
|
||||
for team in TEAMS:
|
||||
new_titles[team] = st.text_input(
|
||||
team,
|
||||
value=titles.get(team, ""),
|
||||
placeholder="예: 슬랙 멘션 자동 분류기",
|
||||
key=f"title_{team}",
|
||||
)
|
||||
if st.form_submit_button("제목 저장"):
|
||||
for team, title in new_titles.items():
|
||||
set_title(team, title)
|
||||
st.success("제목 저장 완료. 투표 페이지에 반영됨.")
|
||||
st.rerun()
|
||||
|
||||
st.divider()
|
||||
st.subheader("📊 분야별 집계")
|
||||
|
||||
@@ -163,16 +222,17 @@ def render_admin():
|
||||
runner_votes = rows[1][1] if len(rows) > 1 else 0
|
||||
diff = winner_votes - runner_votes
|
||||
|
||||
winner_label = fmt_team(winner_team, titles)
|
||||
st.success(
|
||||
f"**우승: {winner_team}** — {winner_votes}표 (2위와 {diff}표 차이)"
|
||||
f"**우승: {winner_label}** — {winner_votes}표 (2위와 {diff}표 차이)"
|
||||
)
|
||||
public_lines.append(
|
||||
f"- {label} 우승: **{winner_team}** ({winner_votes}표, 2위와 {diff}표 차이)"
|
||||
f"- {label} 우승: **{winner_label}** ({winner_votes}표, 2위와 {diff}표 차이)"
|
||||
)
|
||||
|
||||
with st.expander("전체 분포 (진행자만)"):
|
||||
for team, c in rows:
|
||||
st.write(f"- {team}: {c}표")
|
||||
st.write(f"- {fmt_team(team, titles)}: {c}표")
|
||||
|
||||
st.divider()
|
||||
st.subheader("🎤 시상식 발표용 (복사해서 화면 공유)")
|
||||
|
||||
Reference in New Issue
Block a user