Spaces:
Sleeping
Sleeping
""" | |
이벤트 공지사항 생성기 - 완전 버전 | |
그라디오 최신 버전 지원 | |
""" | |
import gradio as gr | |
from datetime import datetime, timedelta | |
from typing import List, Dict, Any | |
import json | |
import tempfile | |
import os | |
class EventGenerator: | |
"""이벤트 생성기 메인 클래스""" | |
def __init__(self): | |
self.monthly_data = { | |
1: { | |
"holidays": ["신정", "설날", "대보름"], | |
"special_days": ["새해", "신년", "다이어트의 달"], | |
"seasons": ["겨울", "새해맞이", "신년다짐"], | |
"trends": ["새해 계획", "다이어트", "정리정돈", "미니멀라이프"], | |
"colors": ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEAA7"] | |
}, | |
2: { | |
"holidays": ["밸런타인데이", "정월대보름"], | |
"special_days": ["초콜릿데이", "로즈데이"], | |
"seasons": ["겨울", "입춘", "매화꽃"], | |
"trends": ["러브", "로맨스", "셀프케어", "따뜻함"], | |
"colors": ["#FF69B4", "#FFB6C1", "#DC143C", "#FFC0CB", "#8B0000"] | |
}, | |
3: { | |
"holidays": ["삼일절", "화이트데이"], | |
"special_days": ["여성의 날", "화이트데이"], | |
"seasons": ["봄", "벚꽃", "춘분", "개화"], | |
"trends": ["봄맞이", "새학기", "벚꽃놀이", "봄나들이"], | |
"colors": ["#FFB6C1", "#98FB98", "#87CEEB", "#F0E68C", "#DDA0DD"] | |
}, | |
4: { | |
"holidays": ["만우절", "식목일"], | |
"special_days": ["블랙데이", "킹데이"], | |
"seasons": ["봄", "벚꽃만개", "꽃구경"], | |
"trends": ["벚꽃축제", "봄피크닉", "새학기적응", "아웃도어"], | |
"colors": ["#FFB6C1", "#98FB98", "#F0E68C", "#DDA0DD", "#87CEEB"] | |
}, | |
5: { | |
"holidays": ["어린이날", "어버이날", "스승의날", "부처님오신날"], | |
"special_days": ["가정의 달", "로즈데이", "감사데이"], | |
"seasons": ["봄", "신록", "야외활동"], | |
"trends": ["가족사랑", "감사", "나들이", "어린이날선물"], | |
"colors": ["#32CD32", "#FFB6C1", "#87CEEB", "#F0E68C", "#DDA0DD"] | |
}, | |
6: { | |
"holidays": ["현충일", "단오"], | |
"special_days": ["키스데이", "패밀리데이"], | |
"seasons": ["초여름", "장마준비", "여름나기"], | |
"trends": ["여름준비", "다이어트", "워터파크", "바캉스준비"], | |
"colors": ["#00CED1", "#FFD700", "#FF6347", "#32CD32", "#FF69B4"] | |
}, | |
7: { | |
"holidays": ["제헌절", "초복", "중복"], | |
"special_days": ["실버데이", "썸머데이"], | |
"seasons": ["여름", "장마", "휴가철"], | |
"trends": ["여름휴가", "워터액티비티", "시원한음식", "휴가패션"], | |
"colors": ["#00BFFF", "#FFD700", "#FF6347", "#32CD32", "#FF69B4"] | |
}, | |
8: { | |
"holidays": ["광복절", "말복"], | |
"special_days": ["그린데이", "썸머바캉스"], | |
"seasons": ["여름", "휴가철", "더위절정"], | |
"trends": ["여름휴가절정", "바다여행", "축제", "여름추억"], | |
"colors": ["#00BFFF", "#FF6347", "#FFD700", "#32CD32", "#FF69B4"] | |
}, | |
9: { | |
"holidays": ["추석", "추분"], | |
"special_days": ["포토데이", "뮤직데이"], | |
"seasons": ["가을", "추석연휴", "선선함"], | |
"trends": ["추석준비", "가을패션", "독서의계절", "문화생활"], | |
"colors": ["#FF8C00", "#DC143C", "#B8860B", "#CD853F", "#D2691E"] | |
}, | |
10: { | |
"holidays": ["개천절", "한글날"], | |
"special_days": ["와인데이", "커피데이"], | |
"seasons": ["가을", "단풍", "쌀쌀함"], | |
"trends": ["가을단풍", "독서", "카페문화", "가을나들이"], | |
"colors": ["#FF8C00", "#DC143C", "#B8860B", "#8B4513", "#A0522D"] | |
}, | |
11: { | |
"holidays": ["빼빼로데이", "수능"], | |
"special_days": ["무비데이", "오렌지데이"], | |
"seasons": ["늦가을", "쌀쌀함", "겨울준비"], | |
"trends": ["빼빼로데이", "수능응원", "겨울준비", "연말준비"], | |
"colors": ["#8B4513", "#A0522D", "#CD853F", "#D2691E", "#FF8C00"] | |
}, | |
12: { | |
"holidays": ["크리스마스", "성탄절", "동지"], | |
"special_days": ["허그데이", "키스데이"], | |
"seasons": ["겨울", "연말", "크리스마스"], | |
"trends": ["크리스마스", "연말파티", "선물", "한해마무리"], | |
"colors": ["#DC143C", "#228B22", "#FFD700", "#800080", "#FF69B4"] | |
} | |
} | |
self.event_templates = { | |
"댓글 달기 이벤트": { | |
"steps": ["해당 게시글 찾아가기", "감성적인 댓글 작성하기", "댓글 등록 완료"], | |
"description": "지정된 게시글에 댓글을 달아주세요" | |
}, | |
"게시글 작성 이벤트": { | |
"steps": ["이벤트 주제 확인하기", "창의적인 게시글 작성하기", "해시태그 포함하여 업로드"], | |
"description": "주제에 맞는 게시글을 작성해주세요" | |
}, | |
"좋아요/공감 이벤트": { | |
"steps": ["이벤트 게시글 확인하기", "좋아요 버튼 클릭하기", "추가 액션 완료하기"], | |
"description": "마음에 드는 게시글에 좋아요를 눌러주세요" | |
}, | |
"출석체크 이벤트": { | |
"steps": ["매일 커뮤니티 접속하기", "출석체크 버튼 클릭하기", "연속 출석 달성하기"], | |
"description": "매일 커뮤니티에 방문하여 출석체크를 해주세요" | |
}, | |
"추천인 이벤트": { | |
"steps": ["초대 링크 생성하기", "친구에게 링크 공유하기", "친구 가입 완료 확인하기"], | |
"description": "친구를 초대하고 함께 이벤트에 참여해주세요" | |
}, | |
"사진 업로드 이벤트": { | |
"steps": ["테마에 맞는 사진 촬영하기", "사진 업로드하기", "설명글과 함께 공유하기"], | |
"description": "테마에 맞는 사진을 업로드해주세요" | |
}, | |
"퀴즈/설문 이벤트": { | |
"steps": ["퀴즈 문제 확인하기", "정답 또는 의견 제출하기", "제출 완료 확인하기"], | |
"description": "퀴즈를 풀거나 설문에 참여해주세요" | |
} | |
} | |
def analyze_monthly_concepts(self, month: int, year: int = 2025) -> List[Dict[str, Any]]: | |
"""월별 컨셉 분석""" | |
month_data = self.monthly_data.get(month, {}) | |
concepts = [] | |
# 컨셉 1: 주요 기념일 기반 | |
if month_data.get("holidays"): | |
main_holiday = month_data["holidays"][0] | |
concepts.append({ | |
"name": f"{main_holiday} 특별 이벤트", | |
"theme": f"{main_holiday}를 테마로 한 감성 이벤트", | |
"catchphrase": f"💕 {main_holiday}과 함께 특별한 추억을 만들어요!", | |
"reason": f"{month}월의 대표 기념일인 {main_holiday}로 높은 관심과 참여도 예상", | |
"colors": month_data.get("colors", ["#FF69B4"])[:3], | |
"participation_score": 8.5, | |
"participation_reason": "주요 기념일로 인한 높은 관심도와 시즌성", | |
"event_style": "댓글 이벤트 + 인증샷 업로드", | |
"competitive_edge": "시즌 특화 컨텐츠로 차별화", | |
"target_appeal": "기념일 감성과 공감대 형성" | |
}) | |
# 컨셉 2: 계절/트렌드 기반 | |
if month_data.get("trends"): | |
trend_theme = month_data["trends"][0] | |
concepts.append({ | |
"name": f"{trend_theme} 챌린지", | |
"theme": f"최신 {trend_theme} 트렌드 참여형 이벤트", | |
"catchphrase": f"✨ 지금 핫한 {trend_theme}! 함께 도전해요!", | |
"reason": f"현재 인기 트렌드인 {trend_theme}로 젊은 층 어필", | |
"colors": month_data.get("colors", ["#4ECDC4"])[1:4], | |
"participation_score": 8.0, | |
"participation_reason": "최신 트렌드로 인한 화제성과 참여욕구", | |
"event_style": "챌린지 참여 + SNS 공유", | |
"competitive_edge": "트렌드 선도적 이벤트", | |
"target_appeal": "트렌드 민감층과 SNS 활용" | |
}) | |
# 컨셉 3: 계절감 기반 | |
if month_data.get("seasons"): | |
season_theme = month_data["seasons"][0] | |
concepts.append({ | |
"name": f"{season_theme} 라이프 이벤트", | |
"theme": f"{season_theme} 계절 맞이 라이프스타일 이벤트", | |
"catchphrase": f"🌸 {season_theme}과 함께하는 일상의 소소한 행복!", | |
"reason": f"{season_theme} 계절감으로 자연스러운 참여 유도", | |
"colors": month_data.get("colors", ["#98FB98"])[2:5], | |
"participation_score": 7.5, | |
"participation_reason": "계절적 공감대와 일상 연관성", | |
"event_style": "포토 챌린지 + 후기 공유", | |
"competitive_edge": "계절별 맞춤 컨텐츠", | |
"target_appeal": "일상 속 계절감과 감성" | |
}) | |
# 컨셉 4: 커뮤니티 소통 기반 | |
concepts.append({ | |
"name": f"{month}월 우리들의 이야기", | |
"theme": "커뮤니티 멤버 간 소통과 공감 이벤트", | |
"catchphrase": f"💬 {month}월, 우리의 특별한 이야기를 들려주세요!", | |
"reason": "커뮤니티 결속력 강화와 지속적 참여 유도", | |
"colors": month_data.get("colors", ["#87CEEB"])[-3:], | |
"participation_score": 7.0, | |
"participation_reason": "커뮤니티 애착도와 소속감 기반 참여", | |
"event_style": "스토리텔링 + 공감 이벤트", | |
"competitive_edge": "진정성 있는 커뮤니티 소통", | |
"target_appeal": "소속감과 공감대 형성" | |
}) | |
# 컨셉 5: 스페셜 데이 기반 | |
if month_data.get("special_days"): | |
special_day = month_data["special_days"][0] | |
concepts.append({ | |
"name": f"{special_day} 스페셜 위크", | |
"theme": f"{special_day} 맞이 특별 혜택 이벤트", | |
"catchphrase": f"🎁 {special_day} 특별한 선물이 기다려요!", | |
"reason": f"{special_day}의 특별함을 활용한 프리미엄 이벤트", | |
"colors": month_data.get("colors", ["#FFB6C1"])[:3], | |
"participation_score": 7.8, | |
"participation_reason": "특별한 날에 대한 기대감과 혜택 관심", | |
"event_style": "미션 완료 + 럭키박스", | |
"competitive_edge": "특별 혜택과 깜짝 이벤트", | |
"target_appeal": "특별함과 혜택에 대한 기대" | |
}) | |
return concepts | |
def generate_event_notice(self, concept_data: Dict, event_type: str, custom_event: str = None) -> str: | |
"""완성된 이벤트 공지사항 생성""" | |
concept_name = concept_data.get("name", "특별 이벤트") | |
catchphrase = concept_data.get("catchphrase", "✨ 특별한 이벤트에 참여하세요!") | |
theme = concept_data.get("theme", "커뮤니티 이벤트") | |
# 이벤트 기간 설정 | |
now = datetime.now() | |
start_date = now.strftime("%Y.%m.%d") | |
end_date = (now + timedelta(days=7)).strftime("%Y.%m.%d") | |
# 템플릿 선택 | |
if custom_event and event_type == "직접 입력": | |
template = { | |
"description": custom_event, | |
"steps": ["이벤트 내용 확인하기", "요구사항에 맞게 참여하기", "참여 완료 확인하기"] | |
} | |
else: | |
template = self.event_templates.get(event_type, self.event_templates["댓글 달기 이벤트"]) | |
# 공지사항 생성 | |
notice = f"""{catchphrase} | |
🎉 {concept_name} 🎉 | |
📅 이벤트 기간: {start_date} ~ {end_date} 23:59 | |
======================== | |
✨ EVENT | |
{theme}을 테마로 한 특별한 이벤트에 참여하세요! | |
많은 분들의 적극적인 참여를 기다리고 있어요 💕 | |
📅 이벤트 기간: {start_date} ~ {end_date} | |
======================== | |
🎯 STEP 1 | |
{template["steps"][0]} | |
* 이벤트 게시글을 꼼꼼히 확인해주세요! | |
🎯 STEP 2 | |
{template["steps"][1]} | |
* 진심이 담긴 따뜻한 마음으로 참여해주세요 | |
🎯 STEP 3 | |
{template["steps"][2]} | |
* 모든 과정을 완료하시면 참여 완료입니다! | |
================= | |
🎁 당첨혜택 | |
✨ 1등 (1명): 스타벅스 5만원 기프트카드 | |
🎉 2등 (3명): 베스킨라빈스 아이스크림 쿠폰 | |
💝 3등 (10명): 편의점 5천원 상품권 | |
🌟 참가상 (50명): 모바일 치킨 쿠폰 | |
📋 추첨 방식: 참여자 중 무작위 추첨 | |
======================== | |
👥 이벤트 대상 | |
✅ 커뮤니티 정회원 (가입 후 7일 경과) | |
✅ 만 18세 이상 성인 | |
✅ 이벤트 기간 내 정상 참여자 | |
(*중복 참여, 어뷰징 계정 제외) | |
======================== | |
📮 수령방법 | |
1️⃣ 당첨자 발표: 이벤트 종료 후 3일 이내 | |
2️⃣ 개별 메시지를 통한 당첨 안내 | |
3️⃣ 본인 확인 후 7일 이내 회신 필수 | |
4️⃣ 모바일 쿠폰/기프트카드 발송 | |
📞 문의처: 커뮤니티 고객센터 | |
🕐 수령 기한: 당첨 안내 후 30일 이내 | |
========================= | |
⚠️ 꼭 확인하세요! | |
※ 부적절한 내용의 댓글/게시글은 삭제될 수 있습니다 | |
※ 중복 계정이나 어뷰징으로 의심되는 참여는 제외됩니다 | |
※ 당첨자는 본인 확인을 위해 추가 정보 요청이 있을 수 있습니다 | |
※ 상품은 현금으로 교환되지 않으며, 재판매가 불가합니다 | |
※ 이벤트 내용은 운영진의 판단에 따라 변경될 수 있습니다 | |
※ 문의사항은 고객센터를 통해 연락해주세요 | |
=========================== | |
📞 고객센터: 1234-5678 | |
🕐 운영시간: 평일 09:30~18:00 (점심시간 12:00~13:00) | |
💬 카카오톡: @event_community | |
※ 주말 및 공휴일 휴무 | |
💝 많은 분들의 참여 부탁드려요! 감사합니다 💕""" | |
return notice.strip() | |
def count_text_stats(self, text: str) -> str: | |
"""텍스트 통계 계산""" | |
total_chars = len(text) | |
chars_no_space = len(text.replace(" ", "").replace("\n", "")) | |
words = len(text.split()) | |
lines = len(text.split("\n")) | |
return f"총 글자수: {total_chars:,}자 | 공백제외: {chars_no_space:,}자 | 단어수: {words:,}개 | 줄수: {lines:,}줄" | |
def create_app(): | |
"""그라디오 앱 생성""" | |
generator = EventGenerator() | |
# 커스텀 CSS | |
custom_css = """ | |
.main-header { | |
text-align: center; | |
padding: 30px; | |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
color: white; | |
border-radius: 15px; | |
margin-bottom: 25px; | |
box-shadow: 0 4px 15px rgba(0,0,0,0.1); | |
} | |
.input-section { | |
background: #f8f9fa; | |
padding: 25px; | |
border-radius: 15px; | |
box-shadow: 0 2px 10px rgba(0,0,0,0.05); | |
margin-bottom: 20px; | |
} | |
.output-section { | |
background: white; | |
padding: 25px; | |
border-radius: 15px; | |
box-shadow: 0 2px 10px rgba(0,0,0,0.05); | |
} | |
.concept-card { | |
background: linear-gradient(45deg, #e3f2fd, #f1f8e9); | |
padding: 20px; | |
border-radius: 12px; | |
margin: 15px 0; | |
border-left: 5px solid #2196f3; | |
box-shadow: 0 2px 8px rgba(0,0,0,0.1); | |
} | |
.stats-box { | |
background: #fff3cd; | |
padding: 10px; | |
border-radius: 8px; | |
border: 1px solid #ffeaa7; | |
margin-top: 10px; | |
} | |
""" | |
with gr.Blocks( | |
title="🎉 AI 이벤트 공지사항 생성기", | |
theme=gr.themes.Soft(), | |
css=custom_css | |
) as demo: | |
# 헤더 | |
with gr.Column(elem_classes="main-header"): | |
gr.Markdown(""" | |
# 🎉 AI 이벤트 공지사항 생성기 | |
### 실시간 트렌드 분석으로 완벽한 이벤트 기획을 도와드립니다 | |
#### 20-40대 여성 타겟 커뮤니티 이벤트 전문 생성기 | |
""") | |
with gr.Row(): | |
# 좌측: 입력 영역 | |
with gr.Column(scale=1, elem_classes="input-section"): | |
gr.Markdown("## ⚙️ 기본 설정") | |
with gr.Row(): | |
year_input = gr.Number( | |
value=2025, | |
label="📅 연도", | |
minimum=2024, | |
maximum=2030, | |
step=1 | |
) | |
month_input = gr.Dropdown( | |
choices=[f"{i}월" for i in range(1, 13)], | |
label="📅 이벤트 월 선택", | |
value=f"{datetime.now().month}월" | |
) | |
analyze_btn = gr.Button( | |
"🧠 AI 컨셉 분석 시작", | |
variant="secondary", | |
size="lg" | |
) | |
# 컨셉 선택 영역 | |
with gr.Group(visible=False) as concept_group: | |
gr.Markdown("### 🎨 추천 컨셉 선택") | |
concept_selector = gr.Dropdown( | |
label="컨셉을 선택해주세요", | |
interactive=True | |
) | |
concept_preview = gr.Textbox( | |
label="📊 선택된 컨셉 미리보기", | |
lines=8, | |
interactive=False | |
) | |
gr.Markdown("## 🎯 이벤트 설정") | |
event_type_selector = gr.Dropdown( | |
choices=[ | |
"댓글 달기 이벤트", | |
"게시글 작성 이벤트", | |
"좋아요/공감 이벤트", | |
"출석체크 이벤트", | |
"추천인 이벤트", | |
"사진 업로드 이벤트", | |
"퀴즈/설문 이벤트", | |
"직접 입력" | |
], | |
label="🎯 이벤트 유형 선택", | |
value="댓글 달기 이벤트" | |
) | |
custom_event_input = gr.Textbox( | |
label="✏️ 커스텀 이벤트 설명", | |
placeholder="'직접 입력' 선택시 원하는 이벤트 내용을 자세히 설명해주세요", | |
visible=False, | |
lines=4 | |
) | |
generate_btn = gr.Button( | |
"✨ 완성된 공지사항 생성하기", | |
variant="primary", | |
size="lg", | |
visible=False | |
) | |
# 우측: 결과 출력 영역 | |
with gr.Column(scale=2, elem_classes="output-section"): | |
with gr.Tabs() as tabs: | |
with gr.TabItem("🧠 AI 컨셉 분석"): | |
concept_analysis_output = gr.Markdown( | |
value="👆 먼저 좌측에서 연도와 월을 선택한 후 'AI 컨셉 분석 시작' 버튼을 클릭해주세요", | |
elem_classes="concept-card" | |
) | |
with gr.TabItem("📝 완성된 공지사항"): | |
final_notice_output = gr.Textbox( | |
label="생성된 이벤트 공지사항", | |
lines=35, | |
placeholder="컨셉 선택 후 '완성된 공지사항 생성하기' 버튼을 클릭하면 결과가 나타납니다", | |
show_copy_button=True, | |
max_lines=50 | |
) | |
# 통계 및 버튼 | |
with gr.Row(): | |
text_stats_output = gr.Textbox( | |
label="📊 텍스트 통계", | |
lines=1, | |
interactive=False, | |
visible=False, | |
elem_classes="stats-box" | |
) | |
with gr.Row(): | |
download_btn = gr.DownloadButton( | |
"💾 텍스트 파일로 다운로드", | |
size="sm", | |
variant="secondary", | |
visible=False | |
) | |
regenerate_btn = gr.Button( | |
"🔄 다시 생성하기", | |
size="sm", | |
visible=False | |
) | |
with gr.TabItem("🎨 컨셉 상세정보"): | |
concept_details_output = gr.JSON( | |
label="선택된 컨셉의 상세 정보", | |
visible=False | |
) | |
# 상태 관리 | |
concepts_state = gr.State([]) | |
selected_concept_state = gr.State(None) | |
# 이벤트 핸들러 함수들 | |
def handle_concept_analysis(year, month): | |
"""AI 컨셉 분석 처리""" | |
try: | |
month_num = int(month.replace('월', '')) | |
concepts = generator.analyze_monthly_concepts(month_num, year) | |
concept_choices = [f"{concept['name']} - {concept['theme']}" for concept in concepts] | |
# 분석 결과 마크다운 생성 | |
analysis_md = f"# 🎯 {year}년 {month} AI 분석 결과\n\n" | |
analysis_md += f"**분석 완료:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n" | |
analysis_md += f"**총 {len(concepts)}개 컨셉 추천**\n\n" | |
for i, concept in enumerate(concepts, 1): | |
colors_display = " ".join([f"`{color}`" for color in concept['colors'][:3]]) | |
analysis_md += f""" | |
## {i}. {concept['name']} | |
**🏷️ 테마:** {concept['theme']} | |
**🎯 타겟 어필:** {concept['target_appeal']} | |
**⭐ 예상 참여도:** {concept['participation_score']}/10점 | |
**🎪 추천 이벤트:** {concept['event_style']} | |
**🎨 색상 팔레트:** {colors_display} | |
**💡 선정 이유:** {concept['reason']} | |
--- | |
""" | |
return ( | |
gr.update(visible=True), # concept_group | |
gr.update(choices=concept_choices, value=concept_choices[0] if concept_choices else None), # concept_selector | |
gr.update(visible=True), # generate_btn | |
analysis_md, # concept_analysis_output | |
concepts # concepts_state | |
) | |
except Exception as e: | |
error_message = f"❌ 컨셉 분석 중 오류가 발생했습니다: {str(e)}" | |
return ( | |
gr.update(visible=False), | |
gr.update(choices=[], value=None), | |
gr.update(visible=False), | |
error_message, | |
[] | |
) | |
def handle_concept_selection(selected_concept_name, concepts_data): | |
"""컨셉 선택 처리""" | |
try: | |
if selected_concept_name and concepts_data: | |
selected_idx = next(i for i, c in enumerate(concepts_data) | |
if f"{c['name']} - {c['theme']}" == selected_concept_name) | |
selected_concept = concepts_data[selected_idx] | |
# 미리보기 텍스트 생성 | |
preview_text = f"""🎨 컨셉명: {selected_concept['name']} | |
🏷️ 테마: {selected_concept['theme']} | |
💬 캐치프레이즈: {selected_concept['catchphrase']} | |
🎯 타겟 어필: {selected_concept['target_appeal']} | |
⭐ 예상 참여도: {selected_concept['participation_score']}/10점 | |
📊 참여 근거: {selected_concept['participation_reason']} | |
🎪 추천 이벤트: {selected_concept['event_style']} | |
🎨 색상 팔레트: {', '.join(selected_concept['colors'][:3])} | |
💡 차별화 포인트: {selected_concept['competitive_edge']}""" | |
return preview_text, selected_concept, gr.update(value=selected_concept, visible=True) | |
return "", None, gr.update(visible=False) | |
except Exception as e: | |
return f"❌ 컨셉 선택 오류: {str(e)}", None, gr.update(visible=False) | |
def handle_event_type_change(event_type): | |
"""이벤트 유형 변경 처리""" | |
return gr.update(visible=(event_type == "직접 입력")) | |
def handle_final_generation(year, month, selected_concept, event_type, custom_event): | |
"""최종 공지사항 생성 처리""" | |
try: | |
if not selected_concept: | |
return ( | |
"❌ 먼저 컨셉을 선택해주세요!", | |
gr.update(visible=False), | |
gr.update(visible=False), | |
gr.update(visible=False), | |
"" | |
) | |
# 공지사항 생성 | |
notice = generator.generate_event_notice( | |
concept_data=selected_concept, | |
event_type=event_type, | |
custom_event=custom_event | |
) | |
# 텍스트 통계 | |
stats = generator.count_text_stats(notice) | |
# 다운로드 파일 준비 | |
def prepare_download(): | |
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
filename = f"event_notice_{timestamp}.txt" | |
temp_file = tempfile.NamedTemporaryFile( | |
mode='w', | |
suffix='.txt', | |
delete=False, | |
encoding='utf-8' | |
) | |
temp_file.write(notice) | |
temp_file.close() | |
return temp_file.name | |
return ( | |
notice, # final_notice_output | |
gr.update(visible=True, value=stats), # text_stats_output | |
gr.update(visible=True), # download_btn | |
gr.update(visible=True), # regenerate_btn | |
prepare_download() # 다운로드 파일 | |
) | |
except Exception as e: | |
error_message = f"❌ 공지사항 생성 중 오류가 발생했습니다: {str(e)}" | |
return ( | |
error_message, | |
gr.update(visible=False), | |
gr.update(visible=False), | |
gr.update(visible=False), | |
"" | |
) | |
def handle_regeneration(year, month, selected_concept, event_type, custom_event): | |
"""공지사항 재생성""" | |
return handle_final_generation(year, month, selected_concept, event_type, custom_event) | |
# 이벤트 바인딩 | |
analyze_btn.click( | |
handle_concept_analysis, | |
inputs=[year_input, month_input], | |
outputs=[concept_group, concept_selector, generate_btn, concept_analysis_output, concepts_state] | |
) | |
concept_selector.change( | |
handle_concept_selection, | |
inputs=[concept_selector, concepts_state], | |
outputs=[concept_preview, selected_concept_state, concept_details_output] | |
) | |
event_type_selector.change( | |
handle_event_type_change, | |
inputs=[event_type_selector], | |
outputs=[custom_event_input] | |
) | |
generate_btn.click( | |
handle_final_generation, | |
inputs=[year_input, month_input, selected_concept_state, event_type_selector, custom_event_input], | |
outputs=[final_notice_output, text_stats_output, download_btn, regenerate_btn, download_btn] | |
) | |
regenerate_btn.click( | |
handle_regeneration, | |
inputs=[year_input, month_input, selected_concept_state, event_type_selector, custom_event_input], | |
outputs=[final_notice_output, text_stats_output, download_btn, regenerate_btn, download_btn] | |
) | |
return demo | |
def main(): | |
"""메인 실행 함수""" | |
print("🎉 이벤트 공지사항 생성기를 시작합니다...") | |
print("📚 그라디오 최신 버전으로 실행 중...") | |
try: | |
# 애플리케이션 생성 | |
demo = create_app() | |
# 서버 실행 | |
print("🚀 서버를 시작합니다...") | |
demo.launch( | |
server_name="0.0.0.0", | |
server_port=7860, | |
share=True, | |
show_tips=True, | |
show_error=True | |
) | |
except Exception as e: | |
print(f"❌ 애플리케이션 시작 중 오류 발생: {e}") | |
import traceback | |
traceback.print_exc() | |
if __name__ == "__main__": | |
main()""" | |
이벤트 공지사항 생성기 - 기본 버전 (MVP) | |
""" | |
import gradio as gr | |
from datetime import datetime | |
from typing import List, Dict, Any | |
class BasicEventGenerator: | |
"""기본 이벤트 생성기""" | |
def __init__(self): | |
self.monthly_data = { | |
1: {"holidays": ["신정", "설날"], "trends": ["새해 계획", "다이어트"], "colors": ["#FF6B6B", "#4ECDC4", "#45B7D1"]}, | |
2: {"holidays": ["밸런타인데이"], "trends": ["러브", "로맨스"], "colors": ["#FF69B4", "#FFB6C1", "#DC143C"]}, | |
3: {"holidays": ["삼일절", "화이트데이"], "trends": ["봄맞이", "벚꽃놀이"], "colors": ["#FFB6C1", "#98FB98", "#87CEEB"]}, | |
4: {"holidays": ["만우절"], "trends": ["벚꽃축제", "봄피크닉"], "colors": ["#98FB98", "#F0E68C", "#DDA0DD"]}, | |
5: {"holidays": ["어린이날", "어버이날"], "trends": ["가족사랑", "나들이"], "colors": ["#32CD32", "#FFB6C1", "#87CEEB"]}, | |
6: {"holidays": ["현충일"], "trends": ["여름준비", "바캉스준비"], "colors": ["#00CED1", "#FFD700", "#FF6347"]}, | |
7: {"holidays": ["제헌절"], "trends": ["여름휴가", "바다여행"], "colors": ["#00BFFF", "#FFD700", "#FF6347"]}, | |
8: {"holidays": ["광복절"], "trends": ["여름휴가절정", "축제"], "colors": ["#00BFFF", "#FF6347", "#FFD700"]}, | |
9: {"holidays": ["추석"], "trends": ["추석준비", "가을패션"], "colors": ["#FF8C00", "#DC143C", "#B8860B"]}, | |
10: {"holidays": ["개천절", "한글날"], "trends": ["가을단풍", "독서"], "colors": ["#FF8C00", "#DC143C", "#B8860B"]}, | |
11: {"holidays": ["빼빼로데이"], "trends": ["빼빼로데이", "겨울준비"], "colors": ["#8B4513", "#A0522D", "#CD853F"]}, | |
12: {"holidays": ["크리스마스"], "trends": ["크리스마스", "연말파티"], "colors": ["#DC143C", "#228B22", "#FFD700"]} | |
} | |
def analyze_month_concepts(self, month: int) -> List[Dict[str, Any]]: | |
"""월별 간단한 컨셉 분석""" | |
month_data = self.monthly_data.get(month, {}) | |
concepts = [] | |
# 컨셉 1: 기념일 기반 | |
if month_data.get("holidays"): | |
holiday = month_data["holidays"][0] | |
concepts.append({ | |
"name": f"{holiday} 특별 이벤트", | |
"theme": f"{holiday}를 테마로 한 이벤트", | |
"score": 8.5, | |
"colors": month_data.get("colors", ["#FF69B4"]) | |
}) | |
# 컨셉 2: 트렌드 기반 | |
if month_data.get("trends"): | |
trend = month_data["trends"][0] | |
concepts.append({ | |
"name": f"{trend} 챌린지", | |
"theme": f"{trend} 트렌드 이벤트", | |
"score": 7.8, | |
"colors": month_data.get("colors", ["#87CEEB"]) | |
}) | |
# 컨셉 3: 커뮤니티 기반 | |
concepts.append({ | |
"name": f"{month}월 소통 이벤트", | |
"theme": "커뮤니티 멤버 소통 이벤트", | |
"score": 7.0, | |
"colors": month_data.get("colors", ["#98FB98"]) | |
}) | |
return concepts | |
def generate_notice(self, concept: Dict, event_type: str) -> str: | |
"""기본 공지사항 생성""" | |
now = datetime.now() | |
start_date = f"{now.year}.{now.month}.{now.day}" | |
end_date = f"{now.year}.{now.month}.{now.day + 7}" | |
notice = f""" | |
💕 {concept['name']}에 참여하세요! | |
🎉 {concept['name']} 🎉 | |
📅 이벤트 기간: {start_date} ~ {end_date} 23:59 | |
======================== | |
✨ EVENT | |
{concept['theme']}을 테마로 한 특별한 이벤트입니다! | |
많은 분들의 적극적인 참여를 기다리고 있어요 💕 | |
======================== | |
🎯 참여 방법 | |
1️⃣ 이벤트 게시글 확인하기 | |
2️⃣ {event_type} 참여하기 | |
3️⃣ 참여 완료! | |
================= | |
🎁 당첨혜택 | |
✨ 1등 (1명): 스타벅스 5만원 기프트카드 | |
🎉 2등 (3명): 베스킨라빈스 쿠폰 | |
💝 3등 (10명): 편의점 5천원 상품권 | |
📋 추첨: 참여자 중 무작위 추첨 | |
======================== | |
👥 이벤트 대상 | |
✅ 커뮤니티 정회원 | |
✅ 만 18세 이상 | |
✅ 이벤트 기간 내 참여자 | |
======================== | |
⚠️ 주의사항 | |
※ 부적절한 참여는 제외됩니다 | |
※ 중복 계정 참여 불가 | |
※ 당첨자는 본인 확인 필요 | |
=========================== | |
📞 문의: 1234-5678 | |
💬 카카오톡: @event_community | |
💝 많은 참여 부탁드려요! 감사합니다 💕 | |
""" | |
return notice.strip() | |
def create_interface(): | |
"""그라디오 인터페이스 생성""" | |
generator = BasicEventGenerator() | |
with gr.Blocks(title="이벤트 공지사항 생성기") as demo: | |
gr.Markdown(""" | |
# 🎉 이벤트 공지사항 생성기 | |
### 간편하게 이벤트 공지사항을 만들어보세요! | |
""") | |
with gr.Row(): | |
with gr.Column(): | |
gr.Markdown("## ⚙️ 설정") | |
month_input = gr.Dropdown( | |
choices=[f"{i}월" for i in range(1, 13)], | |
label="📅 이벤트 월", | |
value=f"{datetime.now().month}월" | |
) | |
event_type_input = gr.Dropdown( | |
choices=["댓글 달기", "게시글 작성", "좋아요 누르기", "출석체크"], | |
label="🎯 이벤트 유형", | |
value="댓글 달기" | |
) | |
analyze_btn = gr.Button("🧠 컨셉 분석하기", variant="secondary") | |
concept_dropdown = gr.Dropdown( | |
label="🎨 컨셉 선택", | |
visible=False | |
) | |
generate_btn = gr.Button("✨ 공지사항 생성하기", variant="primary", visible=False) | |
with gr.Column(): | |
gr.Markdown("## 📝 결과") | |
concept_result = gr.Textbox( | |
label="컨셉 분석 결과", | |
lines=8, | |
placeholder="먼저 '컨셉 분석하기' 버튼을 클릭하세요" | |
) | |
final_result = gr.Textbox( | |
label="생성된 공지사항", | |
lines=20, | |
placeholder="컨셉을 선택하고 '공지사항 생성하기' 버튼을 클릭하세요", | |
show_copy_button=True | |
) | |
# 상태 변수 | |
concepts_state = gr.State([]) | |
selected_concept_state = gr.State(None) | |
def handle_analyze(month): | |
"""컨셉 분석 처리""" | |
try: | |
month_num = int(month.replace('월', '')) | |
concepts = generator.analyze_month_concepts(month_num) | |
# 결과 텍스트 생성 | |
result_text = f"# {month} 추천 컨셉\n\n" | |
concept_choices = [] | |
for i, concept in enumerate(concepts, 1): | |
result_text += f"## {i}. {concept['name']}\n" | |
result_text += f"- 테마: {concept['theme']}\n" | |
result_text += f"- 예상 점수: {concept['score']}/10점\n\n" | |
concept_choices.append(concept['name']) | |
return ( | |
result_text, | |
gr.update(visible=True, choices=concept_choices, value=concept_choices[0]), | |
gr.update(visible=True), | |
concepts | |
) | |
except Exception as e: | |
return f"❌ 오류: {str(e)}", gr.update(visible=False), gr.update(visible=False), [] | |
def handle_concept_select(concept_name, concepts): | |
"""컨셉 선택 처리""" | |
if concept_name and concepts: | |
selected = next((c for c in concepts if c['name'] == concept_name), None) | |
return selected | |
return None | |
def handle_generate(concept, event_type): | |
"""공지사항 생성 처리""" | |
if concept: | |
notice = generator.generate_notice(concept, event_type) | |
return notice | |
return "❌ 컨셉을 먼저 선택해주세요!" | |
# 이벤트 연결 | |
analyze_btn.click( | |
handle_analyze, | |
inputs=[month_input], | |
outputs=[concept_result, concept_dropdown, generate_btn, concepts_state] | |
) | |
concept_dropdown.change( | |
handle_concept_select, | |
inputs=[concept_dropdown, concepts_state], | |
outputs=[selected_concept_state] | |
) | |
generate_btn.click( | |
handle_generate, | |
inputs=[selected_concept_state, event_type_input], | |
outputs=[final_result] | |
) | |
return demo | |
if __name__ == "__main__": | |
demo = create_interface() | |
demo.launch( | |
server_name="0.0.0.0", | |
server_port=7860, | |
share=True | |
) |