From c76ecbaf32a32108b17ba81e5e5f7688bf25e049 Mon Sep 17 00:00:00 2001 From: th-kim0823 Date: Sat, 25 Apr 2026 19:40:33 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B2=B0=EA=B3=BC=EB=AC=BC=20=EC=A0=9C?= =?UTF-8?q?=EB=AA=A9=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?+=20=ED=88=AC=ED=91=9C=20=EB=9D=BC=EB=94=94=EC=98=A4=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DB 테이블 team_titles 추가 - 어드민 페이지에 팀별 제목 입력 폼 (저장 즉시 반영) - 투표 라디오 옵션이 '팀1 — 결과물 제목' 형식으로 표시 - 우승 발표/시상 텍스트에도 제목 포함 - 제목 미입력 시 팀명만 (fallback) Co-Authored-By: Claude Opus 4.7 (1M context) --- app.py | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/app.py b/app.py index f5c07ed..b087dff 100644 --- a/app.py +++ b/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("🎤 시상식 발표용 (복사해서 화면 공유)")