| |
| |
|
|
| """ |
| G-Mission Foundation Model Partnership Dashboard |
| Hugging Face Space 배포 최적화 버전 |
| """ |
|
|
| import os |
| import subprocess |
| import sys |
| import datetime |
| import traceback |
| import gc |
| import smtplib |
| from email.mime.text import MIMEText |
| from email.mime.multipart import MIMEMultipart |
| import ssl |
| import re |
|
|
| |
| def install_packages(): |
| packages = ['gradio>=4.0.0', 'plotly>=5.0.0', 'pandas>=2.0.0', 'openpyxl>=3.0.0'] |
| for package in packages: |
| try: |
| pkg_name = package.split('>=')[0] |
| __import__(pkg_name) |
| except ImportError: |
| print(f"Installing {package}...") |
| subprocess.check_call([sys.executable, "-m", "pip", "install", package, "--quiet"]) |
|
|
| |
| install_packages() |
|
|
| import pandas as pd |
| import gradio as gr |
|
|
| try: |
| import plotly.express as px |
| import plotly.graph_objects as go |
| PLOTLY_AVAILABLE = True |
| except ImportError: |
| PLOTLY_AVAILABLE = False |
|
|
| from typing import Tuple, List, Dict, Any, Optional |
|
|
| |
|
|
| PASTEL_COLORS = { |
| 'primary_palette': ['#FFB3BA', '#FFDFBA', '#FFFFBA', '#BAFFC9', '#BAE1FF', '#E5BAFF', '#FFBAE5'], |
| 'secondary_palette': ['#FFD6D6', '#FFEBD6', '#FFFFD6', '#D6FFE0', '#D6EBFF', '#E0D6FF', '#FFD6EB'], |
| 'value_chain_colors': { |
| 'LLM': '#BAFFC9', |
| 'AI Infra': '#FFB3BA', |
| '경량AI': '#BAE1FF', |
| 'Agentic': '#FFDFBA', |
| 'Platform': '#E5BAFF', |
| '전체': '#FFFFBA' |
| }, |
| 'maturity_colors': { |
| '성숙': '#BAFFC9', |
| '중견': '#BAE1FF', |
| '스타트업': '#FFDFBA', |
| '초기': '#FFB3BA' |
| } |
| } |
|
|
| VALUE_CHAIN_CATEGORIES = ["전체"] |
|
|
| |
|
|
| class GmailEmailSystem: |
| def __init__(self): |
| self.email_history = [] |
| self.smtp_server = "smtp.gmail.com" |
| self.port = 587 |
| |
| |
| self.gmail_email = os.getenv("GMAIL_EMAIL", "jeongkee1967.kim@gmail.com") |
| self.gmail_password = os.getenv("GMAIL_PASSWORD", "") |
| self.recipient_email = os.getenv("RECIPIENT_EMAIL", "jeongkee10@gmission.co.kr") |
| |
| |
| self.is_configured = bool(self.gmail_password) |
| |
| if self.is_configured: |
| print(f"✅ Gmail SMTP 설정 완료: {self.gmail_email}") |
| else: |
| print("⚠️ Gmail SMTP 미설정 - Secrets에 GMAIL_PASSWORD 추가 필요") |
|
|
| def validate_email(self, email: str) -> bool: |
| if pd.isna(email) or str(email).strip() in ['', 'nan', 'NaN', 'N/A']: |
| return False |
| pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' |
| return re.match(pattern, str(email)) is not None |
|
|
| def send_email(self, subject: str, body: str, company_name: str, target_email: str) -> Tuple[bool, str]: |
| try: |
| if not self.is_configured: |
| return False, """⚠️ Gmail SMTP가 설정되지 않았습니다. |
| |
| **설정 방법:** |
| |
| 1. Gmail 앱 비밀번호 생성: |
| - https://myaccount.google.com/apppasswords |
| - 앱: 메일, 기기: 기타 (G-Mission Dashboard) |
| - 16자리 비밀번호 복사 |
| |
| 2. Hugging Face Space Secrets 설정: |
| - Space Settings → Secrets |
| - 다음 3개 변수 추가: |
| * GMAIL_EMAIL = jeongkee1967.kim@gmail.com |
| * GMAIL_PASSWORD = [16자리 앱 비밀번호] |
| * RECIPIENT_EMAIL = jeongkee10@gmission.co.kr |
| |
| 3. Space 재시작: |
| - Settings → Factory Reboot |
| """ |
|
|
| if not self.validate_email(target_email): |
| return False, f"❌ 유효하지 않은 이메일: {target_email}" |
|
|
| |
| message = MIMEMultipart() |
| message["From"] = f"G-Mission Partnership <{self.gmail_email}>" |
| message["To"] = self.recipient_email |
| message["Subject"] = f"[Partnership Proposal] {subject}" |
|
|
| enhanced_body = f""" |
| <html> |
| <head> |
| <style> |
| body {{ |
| font-family: 'Times New Roman', Times, serif; |
| font-size: 14px; |
| line-height: 1.6; |
| color: #333; |
| }} |
| .header {{ |
| font-weight: bold; |
| margin-top: 20px; |
| margin-bottom: 10px; |
| }} |
| .divider {{ |
| border-top: 1px solid #ccc; |
| margin: 20px 0; |
| }} |
| </style> |
| </head> |
| <body> |
| |
| <p>G-Mission Partnership Team,</p> |
| |
| <p>Strategic partnership proposal generated:</p> |
| |
| <div class="divider"></div> |
| |
| <p class="header">TARGET COMPANY</p> |
| |
| <p> |
| <strong>Company:</strong> {company_name}<br> |
| <strong>Email:</strong> {target_email}<br> |
| <strong>Generated:</strong> {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S KST')} |
| </p> |
| |
| <div class="divider"></div> |
| |
| <p class="header">PROPOSAL CONTENT</p> |
| |
| <div style="white-space: pre-wrap;">{body}</div> |
| |
| <div class="divider"></div> |
| |
| <p class="header">NEXT STEPS</p> |
| |
| <ol> |
| <li>Review and customize content</li> |
| <li>Send to: {target_email}</li> |
| <li>Track response</li> |
| <li>Schedule follow-up meeting</li> |
| </ol> |
| |
| <div class="divider"></div> |
| |
| <p style="font-size: 12px; color: #666;"> |
| Generated by G-Mission Partnership Dashboard<br> |
| <a href="https://huggingface.co/spaces/[your-space-name]">https://huggingface.co/spaces/[your-space-name]</a> |
| </p> |
| |
| </body> |
| </html> |
| """ |
|
|
| message.attach(MIMEText(enhanced_body, "html", "utf-8")) |
|
|
| |
| context = ssl.create_default_context() |
| with smtplib.SMTP(self.smtp_server, self.port) as server: |
| server.starttls(context=context) |
| server.login(self.gmail_email, self.gmail_password) |
| server.sendmail(self.gmail_email, self.recipient_email, message.as_string()) |
|
|
| self.record_email_send(company_name, target_email, subject, body[:100]) |
|
|
| return True, f"✅ 이메일 발송 성공!\n\n📧 발신: {self.gmail_email}\n📧 수신: {self.recipient_email}\n🏢 대상: {company_name}" |
|
|
| except smtplib.SMTPAuthenticationError: |
| return False, """❌ Gmail 인증 실패 |
| |
| **해결 방법:** |
| 1. Gmail 2단계 인증 활성화 확인 |
| 2. 앱 비밀번호 재생성 |
| 3. Hugging Face Secrets에 정확한 비밀번호 입력 |
| 4. Space 재시작 |
| |
| 참고: 일반 비밀번호가 아닌 '앱 비밀번호' 필요""" |
| except Exception as e: |
| return False, f"❌ 발송 실패: {str(e)}" |
|
|
| def create_partnership_email(self, company_name: str, email: str, value_chain: str, |
| maturity: str = "", country: str = "", website: str = "", |
| primary_business: str = "", target_customer: str = "", |
| opensource_type: str = "", rag_agentic: str = "") -> Tuple[str, str, str]: |
| try: |
| if not self.validate_email(email): |
| return "", "", "" |
|
|
| subject = f"Strategic AI Partnership: {company_name} × G-Mission" |
|
|
| |
| gmission_core = """ |
| G-MISSION: ASIAN MARKET LEADER IN ENTERPRISE AI |
| |
| Core Strengths: |
| • 500+ Enterprise Customers: Government, financial, and corporate sectors in Korea |
| • AI Solutions: AI FAX, Vision AI, Document Automation, Agentic AI |
| • Asian Network: Strong presence in Japan, Indonesia, and APAC region |
| • Technical Excellence: LLM fine-tuning, AI pipeline framework, open-source optimization |
| """ |
|
|
| |
| partnership_models = """ |
| PARTNERSHIP MODELS: |
| |
| 1. White-Label Partnership |
| - G-Mission branded distribution in Asian markets |
| - Revenue sharing based on regional performance |
| |
| 2. Joint Development |
| - Co-develop industry solutions |
| - Shared IP and go-to-market strategy |
| |
| 3. Technology Integration & OEM |
| - Integration with G-Mission's Agentic AI platform |
| - OEM licensing for enterprise solutions |
| |
| 4. Market Expansion Alliance |
| - G-Mission as preferred partner for Korea/APAC |
| - Joint sales leveraging 500+ customer network |
| |
| 5. R&D Collaboration |
| - Joint research initiatives |
| - Shared access to enterprise use cases |
| """ |
|
|
| |
| next_steps = """ |
| PROPOSED NEXT STEPS: |
| |
| 1. Initial Partnership Discussion (Virtual) |
| - Overview of capabilities and alignment |
| - Discussion of partnership models |
| |
| 2. Technical Deep-Dive |
| - Integration assessment |
| - Architecture and deployment planning |
| |
| 3. Business Terms |
| - Partnership structure and revenue sharing |
| - Geographic territories |
| |
| 4. Pilot Project |
| - 2-3 enterprise customer deployments |
| - Success metrics and monitoring |
| |
| 5. Full Partnership Agreement |
| - Comprehensive contract |
| - Joint business plan and governance |
| """ |
|
|
| body = f"""Dear {company_name} Partnership Team, |
| |
| We would like to propose a strategic partnership leveraging our complementary strengths in the Asian AI market. |
| |
| {gmission_core} |
| |
| VALUE PROPOSITION: |
| |
| Why Partner with G-Mission: |
| • Immediate access to 500+ enterprise customers |
| • Proven Asian market expertise and relationships |
| • Technical integration and fine-tuning capabilities |
| • Reduced time-to-market with established infrastructure |
| • Revenue growth through existing sales pipeline |
| |
| {partnership_models} |
| |
| {next_steps} |
| |
| MEETING REQUEST: |
| |
| We would be honored to schedule a partnership discussion at your convenience. Our team can present: |
| • Market analysis and opportunity assessment |
| • Technical integration roadmap |
| • Partnership framework and commercial terms |
| • Customer case studies |
| |
| Would your team be available for a virtual meeting in the next two weeks? |
| |
| Best regards, |
| |
| Jeong Kee Kim |
| Chief Partnership Officer |
| G-Mission Corporation |
| |
| Email: jeongkee1967.kim@gmail.com |
| Website: https://www.gmission.com |
| |
| --- |
| Generated: {datetime.datetime.now().strftime('%B %d, %Y')} |
| Company Stage: {maturity if maturity else 'Growth-stage'} |
| Partnership Focus: {value_chain if value_chain else 'Strategic Alliance'} |
| """ |
|
|
| return subject, body, str(email) |
|
|
| except Exception as e: |
| print(f"이메일 생성 오류: {e}") |
| return "", "", "" |
|
|
| def record_email_send(self, company_name: str, email: str, subject: str, body_preview: str) -> bool: |
| try: |
| self.email_history.append({ |
| 'timestamp': datetime.datetime.now(), |
| 'company_name': str(company_name), |
| 'target_email': str(email), |
| 'subject': str(subject), |
| 'body_preview': str(body_preview), |
| 'recipient': self.recipient_email, |
| 'status': '발송 완료', |
| 'send_date': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
| }) |
| return True |
| except: |
| return False |
|
|
| def get_email_history_text(self) -> str: |
| if not self.email_history: |
| config_status = "✅ 설정됨" if self.is_configured else "❌ 미설정" |
| return f"""📧 이메일 발송 현황 |
| |
| **Gmail SMTP 상태:** {config_status} |
| **발신 이메일:** {self.gmail_email} |
| **수신 이메일:** {self.recipient_email} |
| |
| 아직 발송된 이메일이 없습니다. |
| |
| **사용 방법:** |
| 1. 기업 분석 실행 |
| 2. 이메일 탭에서 내용 확인 |
| 3. '이메일 발송' 버튼 클릭 |
| """ |
|
|
| history_text = f"📧 이메일 발송 현황 (총 {len(self.email_history)}건)\n\n" |
| for i, record in enumerate(reversed(self.email_history), 1): |
| history_text += f""" |
| **{i}. {record['company_name']}** |
| - 발송: {record['send_date']} |
| - 대상: {record['target_email']} |
| - 수신: {record['recipient']} |
| - 상태: ✅ {record['status']} |
| |
| --- |
| """ |
| return history_text |
|
|
| |
|
|
| class OptimizedDataProcessor: |
| @staticmethod |
| def process_data(df: pd.DataFrame) -> pd.DataFrame: |
| try: |
| if df is None or df.empty: |
| return pd.DataFrame() |
|
|
| df.columns = df.columns.str.strip() |
| |
| required_columns = ['회사명', 'Value Chain'] |
| for col in required_columns: |
| if col not in df.columns: |
| print(f"⚠️ 필수 컬럼 누락: {col}") |
| return pd.DataFrame() |
|
|
| processed_df = df.copy().fillna('정보 없음') |
|
|
| if 'Value Chain' in processed_df.columns: |
| processed_df['Value Chain'] = processed_df['Value Chain'].astype(str).str.strip() |
|
|
| |
| for col in processed_df.columns: |
| if processed_df[col].dtype == 'object': |
| unique_ratio = len(processed_df[col].unique()) / len(processed_df) |
| if unique_ratio < 0.5: |
| processed_df[col] = processed_df[col].astype('category') |
|
|
| return processed_df |
|
|
| except Exception as e: |
| print(f"데이터 처리 오류: {e}") |
| return df if df is not None else pd.DataFrame() |
|
|
| |
|
|
| class PastelChartGenerator: |
| @staticmethod |
| def create_value_chain_optimized_charts(df: pd.DataFrame, value_chain_filter: str = "전체") -> Tuple[Any, Any, Any, Any, str]: |
| try: |
| if not PLOTLY_AVAILABLE or df is None or df.empty: |
| return None, None, None, None, "데이터가 없습니다." |
|
|
| if value_chain_filter != "전체": |
| filtered_df = df[df['Value Chain'] == value_chain_filter].copy() |
| else: |
| filtered_df = df.copy() |
|
|
| if filtered_df.empty: |
| return None, None, None, None, f"{value_chain_filter} 데이터가 없습니다." |
|
|
| total_companies = len(filtered_df) |
| mature_companies = len(filtered_df[filtered_df['성장단계'] == '성숙']) if '성장단계' in filtered_df.columns else 0 |
| countries = filtered_df['국가/지역'].nunique() if '국가/지역' in filtered_df.columns else 0 |
|
|
| summary_text = f"""### 📊 분석 요약 |
| |
| **총 기업 수:** {total_companies}개 |
| **성숙 단계:** {mature_companies}개 |
| **참여 국가:** {countries}개국""" |
|
|
| |
| if value_chain_filter == "전체" and 'Value Chain' in filtered_df.columns: |
| vc_counts = filtered_df['Value Chain'].value_counts() |
| fig1 = px.pie(values=vc_counts.values, names=vc_counts.index, |
| title=f"Value Chain 분포 ({total_companies}개)", |
| color_discrete_sequence=PASTEL_COLORS['primary_palette']) |
| else: |
| fig1 = go.Figure(data=[go.Pie(labels=[value_chain_filter], values=[total_companies])]) |
| fig1.update_layout(title=f"{value_chain_filter} ({total_companies}개)") |
|
|
| fig1.update_layout(height=400) |
|
|
| |
| if '성장단계' in filtered_df.columns: |
| maturity_counts = filtered_df['성장단계'].value_counts() |
| if not maturity_counts.empty: |
| fig2 = go.Figure(data=[go.Bar(x=maturity_counts.index, y=maturity_counts.values, |
| marker_color=PASTEL_COLORS['primary_palette'])]) |
| fig2.update_layout(title="성장단계별 분포", height=400) |
| else: |
| fig2 = go.Figure() |
| else: |
| fig2 = go.Figure() |
|
|
| |
| fig3 = go.Figure() |
| if '국가/지역' in filtered_df.columns: |
| country_counts = filtered_df['국가/지역'].value_counts().head(10) |
| if not country_counts.empty: |
| fig3 = px.bar(x=country_counts.values, y=country_counts.index, orientation='h', |
| title="국가별 분포", color_discrete_sequence=PASTEL_COLORS['primary_palette']) |
| fig3.update_layout(height=400) |
|
|
| |
| fig4 = go.Figure() |
| if '오픈소스/상용' in filtered_df.columns: |
| oss_counts = filtered_df['오픈소스/상용'].value_counts() |
| if not oss_counts.empty: |
| fig4 = px.pie(values=oss_counts.values, names=oss_counts.index, |
| title="오픈소스/상용 분포", |
| color_discrete_sequence=PASTEL_COLORS['secondary_palette']) |
| fig4.update_layout(height=400) |
|
|
| return fig1, fig2, fig3, fig4, summary_text |
|
|
| except Exception as e: |
| print(f"차트 생성 오류: {e}") |
| return None, None, None, None, f"오류: {str(e)}" |
|
|
| |
|
|
| def analyze_company(df: pd.DataFrame, company_name: str, email_system: GmailEmailSystem) -> Tuple[str, str, str, str]: |
| try: |
| if df is None or df.empty or not company_name: |
| return "기업을 선택해주세요.", "", "", "" |
|
|
| company_data = df[df['회사명'] == company_name] |
| if company_data.empty: |
| return f"'{company_name}' 기업을 찾을 수 없습니다.", "", "", "" |
|
|
| data = company_data.iloc[0] |
|
|
| def safe_get(field, default='정보 없음'): |
| value = data.get(field, default) |
| if pd.isna(value) or str(value).strip() in ['', 'nan', 'NaN', 'N/A']: |
| return default |
| return str(value) |
|
|
| company_name_clean = safe_get('회사명') |
| value_chain = safe_get('Value Chain') |
| maturity = safe_get('성장단계') |
| email_address = safe_get('이메일') |
| country = safe_get('국가/지역') |
| website = safe_get('공식URL') |
|
|
| analysis = f"""# 🏢 {company_name_clean} 분석 |
| |
| ## 기본 정보 |
| - **Value Chain:** {value_chain} |
| - **성장 단계:** {maturity} |
| - **국가:** {country} |
| - **웹사이트:** {website} |
| - **이메일:** {email_address} |
| |
| ## 기술 정보 |
| - **주요사업:** {safe_get('주요사업')} |
| - **모델/플랫폼:** {safe_get('파운데이션/특화모델')} |
| - **배포방식:** {safe_get('배포방식')} |
| - **오픈소스:** {safe_get('오픈소스/상용')} |
| |
| ## 비즈니스 |
| - **주요고객:** {safe_get('주요고객타겟')} |
| - **산업도메인:** {safe_get('주력산업도메인')} |
| - **비즈니스모델:** {safe_get('라이선스/비즈니스모델')} |
| |
| ## G-Mission 파트너십 제안 |
| |
| **협력 방향:** |
| - 500+ 고객 네트워크 활용 |
| - 아시아 시장 공동 진출 |
| - 기술 통합 및 공동 개발 |
| - White-label 또는 OEM 파트너십 |
| |
| 📧 이메일 탭에서 맞춤형 파트너십 제안서를 확인하세요.""" |
|
|
| if email_system.validate_email(email_address): |
| subject, body, recipient = email_system.create_partnership_email( |
| company_name_clean, email_address, value_chain, maturity, country, website, |
| safe_get('주요사업'), safe_get('주요고객타겟'), |
| safe_get('오픈소스/상용'), safe_get('RAG/Agentic') |
| ) |
| return analysis, subject, body, recipient |
| else: |
| return analysis, "", "", "" |
|
|
| except Exception as e: |
| return f"분석 오류: {str(e)}", "", "", "" |
|
|
| |
|
|
| def process_uploaded_file(file): |
| if file is None: |
| return None, "파일을 업로드해주세요." |
|
|
| try: |
| file_path = file.name if hasattr(file, 'name') else str(file) |
|
|
| if file_path.endswith('.csv'): |
| df = pd.read_csv(file_path, encoding='utf-8-sig') |
| elif file_path.endswith(('.xlsx', '.xls')): |
| df = pd.read_excel(file_path) |
| else: |
| return None, "CSV 또는 Excel 파일만 지원합니다." |
|
|
| if df.empty: |
| return None, "빈 파일입니다." |
|
|
| processed_df = OptimizedDataProcessor.process_data(df) |
| |
| if processed_df.empty: |
| return None, "데이터 처리 실패." |
|
|
| success_msg = f"""✅ 파일 처리 완료! |
| - 총 기업: {len(processed_df)}개 |
| - 컬럼: {len(processed_df.columns)}개 |
| - Value Chain: {processed_df['Value Chain'].nunique() if 'Value Chain' in processed_df.columns else 0}개""" |
|
|
| del df |
| gc.collect() |
|
|
| return processed_df, success_msg |
|
|
| except Exception as e: |
| return None, f"오류: {str(e)}" |
|
|
| |
|
|
| def create_dashboard(): |
| email_system = GmailEmailSystem() |
|
|
| with gr.Blocks(title="G-Mission Partnership Dashboard", theme=gr.themes.Soft()) as demo: |
| |
| gr.Markdown(""" |
| # 🚀 G-Mission Foundation Model Partnership Dashboard |
| |
| **500+ 고객 네트워크 기반 전략적 파트너십 분석 시스템** |
| |
| - AI FAX, Vision AI, Agentic AI 솔루션 보유 |
| - 일본, 인도네시아 등 아시아 시장 강력한 네트워크 |
| - LLM Fine-tuning 및 AI Pipeline Framework 전문성 |
| """) |
|
|
| df_state = gr.State(value=None) |
|
|
| with gr.Row(): |
| with gr.Column(): |
| file_upload = gr.File(label="📁 데이터 업로드", file_types=['.csv', '.xlsx', '.xls']) |
| upload_status = gr.Markdown("파일을 업로드해주세요.") |
|
|
| with gr.Tabs(): |
| with gr.TabItem("📊 시장 분석"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| market_filter = gr.Dropdown(choices=["전체"], value="전체", label="Value Chain 필터") |
| market_btn = gr.Button("분석 실행", variant="primary", size="lg") |
| market_summary = gr.Markdown("분석을 실행하세요.") |
| |
| with gr.Row(): |
| with gr.Column(): |
| chart1 = gr.Plot(label="Value Chain 분포") |
| with gr.Column(): |
| chart2 = gr.Plot(label="성장단계") |
| |
| with gr.Row(): |
| with gr.Column(): |
| chart3 = gr.Plot(label="국가별 분포") |
| with gr.Column(): |
| chart4 = gr.Plot(label="오픈소스/상용") |
|
|
| with gr.TabItem("🏢 기업 분석"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| company_filter = gr.Dropdown(choices=["전체"], value="전체", label="Value Chain 필터") |
| company_dropdown = gr.Dropdown(choices=[], label="기업 선택") |
| analyze_btn = gr.Button("분석 실행", variant="primary", size="lg") |
| |
| with gr.Column(scale=2): |
| company_analysis = gr.Markdown("기업을 선택하고 분석하세요.") |
|
|
| with gr.TabItem("📧 이메일"): |
| gr.Markdown(f""" |
| ## Gmail SMTP 이메일 시스템 |
| |
| **현재 상태:** {'✅ 설정됨' if email_system.is_configured else '❌ 미설정'} |
| |
| **설정 방법:** |
| 1. Gmail 앱 비밀번호 생성: https://myaccount.google.com/apppasswords |
| 2. Hugging Face Space Settings → Secrets 추가: |
| - GMAIL_EMAIL = jeongkee1967.kim@gmail.com |
| - GMAIL_PASSWORD = [16자리 앱 비밀번호] |
| - RECIPIENT_EMAIL = jeongkee10@gmission.co.kr |
| 3. Space 재시작 |
| """) |
| |
| email_recipient = gr.Textbox(label="대상 이메일", placeholder="기업 분석 후 자동 입력") |
| email_subject = gr.Textbox(label="제목", placeholder="자동 생성됨") |
| email_body = gr.Textbox(label="내용", lines=15, placeholder="자동 생성됨") |
| send_btn = gr.Button("📧 이메일 발송", variant="primary", size="lg") |
| send_status = gr.Markdown("기업 분석 후 이메일이 생성됩니다.") |
|
|
| with gr.TabItem("📋 발송 현황"): |
| refresh_btn = gr.Button("🔄 새로고침", variant="primary") |
| history_display = gr.Markdown(email_system.get_email_history_text()) |
|
|
| |
| def handle_upload(file): |
| try: |
| df, status = process_uploaded_file(file) |
| if df is not None: |
| companies = sorted(df['회사명'].unique().tolist()) |
| value_chains = ['전체'] + sorted(df['Value Chain'].unique().tolist()) if 'Value Chain' in df.columns else ['전체'] |
| return (df, status, |
| gr.update(choices=companies, value=None), |
| gr.update(choices=value_chains, value="전체"), |
| gr.update(choices=value_chains, value="전체")) |
| else: |
| return None, status, gr.update(choices=[]), gr.update(choices=['전체']), gr.update(choices=['전체']) |
| except Exception as e: |
| return None, f"오류: {e}", gr.update(choices=[]), gr.update(choices=['전체']), gr.update(choices=['전체']) |
|
|
| file_upload.upload( |
| fn=handle_upload, |
| inputs=[file_upload], |
| outputs=[df_state, upload_status, company_dropdown, market_filter, company_filter] |
| ) |
|
|
| market_btn.click( |
| fn=lambda df, f: PastelChartGenerator.create_value_chain_optimized_charts(df, f) if df is not None else (None,None,None,None,"데이터 없음"), |
| inputs=[df_state, market_filter], |
| outputs=[chart1, chart2, chart3, chart4, market_summary] |
| ) |
|
|
| def update_companies(df, filter_val): |
| if df is None or df.empty: |
| return gr.update(choices=[]) |
| filtered = df if filter_val == "전체" else df[df['Value Chain'] == filter_val] |
| return gr.update(choices=sorted(filtered['회사명'].unique().tolist()), value=None) |
|
|
| company_filter.change(fn=update_companies, inputs=[df_state, company_filter], outputs=[company_dropdown]) |
|
|
| analyze_btn.click( |
| fn=lambda df, c: analyze_company(df, c, email_system), |
| inputs=[df_state, company_dropdown], |
| outputs=[company_analysis, email_subject, email_body, email_recipient] |
| ) |
|
|
| def handle_send(recipient, subject, body): |
| if not email_system.validate_email(recipient): |
| return "유효한 이메일 주소가 필요합니다." |
| if not subject or not body: |
| return "제목과 내용이 필요합니다." |
| |
| company = recipient.split('@')[0].capitalize() |
| success, msg = email_system.send_email(subject, body, company, recipient) |
| return msg |
|
|
| send_btn.click(fn=handle_send, inputs=[email_recipient, email_subject, email_body], outputs=[send_status]) |
| refresh_btn.click(fn=lambda: email_system.get_email_history_text(), outputs=[history_display]) |
|
|
| return demo |
|
|
| |
|
|
| if __name__ == "__main__": |
| print("🚀 G-Mission Partnership Dashboard 시작") |
| print("✅ Hugging Face Space 배포 최적화") |
| |
| try: |
| demo = create_dashboard() |
| demo.launch( |
| server_name="0.0.0.0", |
| server_port=7860, |
| show_error=True |
| ) |
| print("✅ 대시보드 실행 성공") |
| except Exception as e: |
| print(f"❌ 실행 오류: {e}") |
| traceback.print_exc() |
|
|