jeongkee's picture
Upload app.py
5a32b6f verified
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
허깅페이스 배포용 Agentic AI 글로벌 파트너 DB 분석 대시보드
✅ Gmail SMTP 이메일 발송
✅ 파스텔톤 차트 색상
✅ 생태계별 최적화된 대시보드
✅ jeongkee10@gmission.com 수신
"""
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])
# 패키지 설치 실행
try:
install_packages()
print("✅ 패키지 설치 완료")
except Exception as e:
print(f"⚠️ 패키지 설치 오류: {e}")
import pandas as pd
import gradio as gr
# Gradio 버전 호환성 확인
try:
gradio_version = gr.__version__
print(f"✅ Gradio 버전: {gradio_version}")
except:
print("⚠️ Gradio 버전 확인 불가")
try:
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
PLOTLY_AVAILABLE = True
print("✅ Plotly 사용 가능")
except ImportError:
print("⚠️ Plotly 없음 - 차트 기능 제한")
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'],
'ecosystem_colors': {
'Infra': '#FFB3BA', # 파스텔 핑크
'Foundation Model': '#BAFFC9', # 파스텔 그린
'Platform / Agentic AI Pipeline': '#BAE1FF', # 파스텔 블루
'Application': '#FFDFBA', # 파스텔 오렌지
'전체': '#E5BAFF' # 파스텔 퍼플
},
'importance_colors': {
5: '#FFB3BA', # S급 - 파스텔 핑크
4: '#FFDFBA', # A급 - 파스텔 오렌지
3: '#BAE1FF', # B급 - 파스텔 블루
2: '#BAFFC9', # C급 - 파스텔 그린
1: '#E5E5E5' # D급 - 파스텔 그레이
}
}
# 생태계 카테고리
ECOSYSTEM_CATEGORIES = [
"전체",
"Infra",
"Foundation Model",
"Platform / Agentic AI Pipeline",
"Application"
]
# 생태계별 최적화된 컬럼 매핑
ECOSYSTEM_OPTIMIZED_COLUMNS = {
'Infra': {
'primary': ['기업명', '중요도', 'HQ_국가', '설립연도', 'PrimaryLLM', 'LLM_Deployment', '주요제품'],
'charts': ['중요도_분포', '국가별_분포', '설립연도_분포', 'LLM_배포방식'],
'focus': 'infrastructure and deployment capabilities'
},
'Foundation Model': {
'primary': ['기업명', '중요도', 'HQ_국가', 'PrimaryLLM', 'OpenSource여부', 'LLM_Deployment', '창업자'],
'charts': ['중요도_분포', 'OpenSource_분포', 'LLM_종류', '국가별_분포'],
'focus': 'model development and open-source contributions'
},
'Platform / Agentic AI Pipeline': {
'primary': ['기업명', '중요도', 'Application_Domain', 'PrimaryLLM', 'STT_TTS지원', 'TargetCustomer', 'PricingModel'],
'charts': ['중요도_분포', '응용도메인_분포', 'STT_TTS_지원', '타겟고객'],
'focus': 'platform integration and agentic workflows'
},
'Application': {
'primary': ['기업명', '중요도', 'Application_Domain', 'TargetCustomer', 'PricingModel', 'KoreaSupport', 'STT_TTS지원'],
'charts': ['중요도_분포', '응용도메인_분포', '가격정책', '한국지원'],
'focus': 'end-user applications and market solutions'
}
}
# 컬럼 매핑
EXACT_COLUMN_MAPPING = {
'기업명': ['기업명', 'CompanyName', 'company', 'name'],
'중요도': ['중요도', 'Importance', 'Priority'],
'Ecosystem_Category': ['Ecosystem Category', 'Ecosystem_Category'],
'HQ_국가': ['HQ_국가', 'HQ_Country', 'Country'],
'설립연도': ['설립연도', 'FoundedYear', 'Founded'],
'주요투자자': ['주요투자자', 'KeyInvestors', 'Investors'],
'기업개요': ['기업개요', 'CompanySummary', 'Summary'],
'웹사이트': ['웹사이트', 'Website', 'WebsiteURL'],
'문의URL': ['문의URL', 'ContactURL', 'Contact_URL'],
'이메일': ['이메일', 'Email', 'EmailAddress'],
'전화번호': ['전화번호', 'PhoneNumber', 'phone'],
'HQ_도시': ['HQ_도시', 'HQ_City', 'City'],
'창업자': ['창업자', 'Founders', 'founder'],
'Application_Domain': ['Application_Domain', 'Application Domain'],
'PrimaryLLM': ['PrimaryLLM', 'Primary LLM'],
'LLM_Deployment': ['LLM_Deployment', 'LLM Deployment'],
'OpenSource여부': ['OpenSource여부', 'OpenSource'],
'주요제품': ['주요제품', 'KeyProducts', 'Products'],
'STT_TTS지원': ['STT_TTS지원', 'STT_TTS'],
'TargetCustomer': ['TargetCustomer', 'Target Customer'],
'PricingModel': ['PricingModel', 'Pricing Model'],
'KoreaSupport': ['KoreaSupport', 'Korea Support'],
'PartnershipProgram': ['PartnershipProgram', 'Partnership Program']
}
# ==================== Gmail SMTP 이메일 시스템 ====================
class GmailEmailSystem:
def __init__(self):
self.email_history = []
self.smtp_server = "smtp.gmail.com"
self.port = 587
# 허깅페이스 Secrets에서 이메일 정보 가져오기
self.gmail_email = os.getenv("GMAIL_EMAIL", "")
self.gmail_password = os.getenv("GMAIL_PASSWORD", "")
self.recipient_email = os.getenv("RECIPIENT_EMAIL", "jeongkee10@gmission.com")
print(f"📧 Gmail 설정: {self.gmail_email[:5] if self.gmail_email else '미설정'}***@gmail.com → {self.recipient_email}")
def validate_email(self, email: str) -> bool:
"""이메일 주소 유효성 검증"""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
def send_email(self, subject: str, body: str, company_name: str, target_email: str) -> Tuple[bool, str]:
"""Gmail SMTP를 통한 실제 이메일 발송"""
try:
if not self.gmail_email or not self.gmail_password:
return False, "Gmail 계정 정보가 설정되지 않았습니다. 허깅페이스 Secrets를 확인하세요."
if not self.validate_email(target_email):
return False, f"올바르지 않은 이메일 주소: {target_email}"
# 이메일 메시지 생성
message = MIMEMultipart()
message["From"] = self.gmail_email
message["To"] = self.recipient_email # 항상 jeongkee10@gmission.com으로 발송
message["Subject"] = f"[파트너십 제안] {subject}"
# 이메일 본문 (원본 대상과 내용 포함)
enhanced_body = f"""
G-Mission 파트너십 팀에게,
다음 기업에 대한 파트너십 제안서가 대시보드에서 생성되었습니다:
원본 대상 기업: {company_name}
원본 이메일 주소: {target_email}
생성 시간: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
=== 파트너십 제안 내용 ===
{body}
=== 다음 단계 ===
1. 제안 내용 검토
2. 필요시 내용 수정
3. {target_email}로 직접 발송
4. 결과 추적 및 관리
---
이 이메일은 Agentic AI 파트너 DB 분석 대시보드에서 자동 생성되었습니다.
"""
message.attach(MIMEText(enhanced_body, "plain", "utf-8"))
# Gmail SMTP 서버 연결 및 발송
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"✅ 이메일이 {self.recipient_email}로 성공적으로 발송되었습니다!"
except smtplib.SMTPAuthenticationError:
return False, "❌ Gmail 인증 실패. 앱 비밀번호를 확인하세요."
except smtplib.SMTPException as e:
return False, f"❌ SMTP 오류: {str(e)}"
except Exception as e:
return False, f"❌ 이메일 발송 실패: {str(e)}"
def create_partnership_email(self, company_name: str, email: str, ecosystem_category: str,
importance: int = 3, country: str = "", website: str = "") -> Tuple[str, str, str]:
"""개선된 파트너십 이메일 생성"""
try:
if not email or '@' not in str(email) or str(email) in ['정보 없음', 'N/A']:
return "", "", ""
# 생태계별 맞춤 제목
ecosystem_subjects = {
"Infra": f"Infrastructure Partnership Opportunity - {company_name} & G-Mission",
"Foundation Model": f"AI Foundation Model Collaboration - {company_name} & G-Mission",
"Platform / Agentic AI Pipeline": f"Agentic AI Platform Partnership - {company_name} & G-Mission",
"Application": f"AI Application Partnership Proposal - {company_name} & G-Mission"
}
subject = ecosystem_subjects.get(ecosystem_category,
f"Strategic AI Partnership Proposal - {company_name} & G-Mission")
# 중요도별 접근 방식
importance_approaches = {
5: "We have identified your company as a top-tier strategic partner",
4: "We have identified your company as a key strategic partner",
3: "We have identified your company as a promising strategic partner",
2: "We are interested in exploring partnership opportunities",
1: "We would like to discuss potential collaboration opportunities"
}
approach = importance_approaches.get(int(importance),
"We are interested in exploring partnership opportunities")
# 생태계별 협력 방안
ecosystem_focus = {
"Infra": """
INFRASTRUCTURE COLLABORATION FOCUS:
• AI compute infrastructure optimization and resource sharing
• Cloud deployment strategies for Korean market
• Hardware-software integration solutions
• Scalable AI infrastructure development""",
"Foundation Model": """
FOUNDATION MODEL COLLABORATION FOCUS:
• Korean language model development and fine-tuning
• Model licensing and technology transfer opportunities
• Joint research in multilingual AI capabilities
• Open-source collaboration initiatives""",
"Platform / Agentic AI Pipeline": """
PLATFORM INTEGRATION FOCUS:
• API integration and interoperability solutions
• Agentic workflow automation partnerships
• Platform ecosystem development
• Enterprise AI orchestration solutions""",
"Application": """
APPLICATION PARTNERSHIP FOCUS:
• Market-specific AI application development
• Customer success case studies and joint marketing
• Industry-vertical solution partnerships
• Korean market entry and expansion strategies"""
}
collaboration_details = ecosystem_focus.get(ecosystem_category,
"• Strategic technology partnership development")
current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
body = f"""Dear {company_name} Partnership Team,
Greetings from G-Mission!
{approach} in the '{ecosystem_category}' space through our comprehensive analysis of 155+ global Agentic AI companies.
{collaboration_details}
KEY PARTNERSHIP OPPORTUNITIES:
• Technology integration and joint product development
• Market expansion in Korea and APAC region
• Strategic alliance in enterprise AI solutions
• Investment and business development collaboration
ABOUT G-MISSION:
G-Mission is a leading AI technology company focused on developing innovative solutions for the Korean and APAC markets. We specialize in enterprise AI implementations and strategic partnerships with global technology leaders.
NEXT STEPS:
We would be delighted to schedule an introductory partnership discussion to explore mutual opportunities. Our team can share detailed market insights, partnership frameworks, and collaboration proposals tailored to your strategic objectives.
Would you be available for a partnership discussion in the coming weeks?
Best regards,
G-Mission Partnership Development Team
Email: jeongkee10@gmission.com
Website: https://gmission.com
---
This partnership proposal was generated through our Agentic AI Global Partner Database Analysis.
Generated on: {current_time}
Analysis Confidence: {"High" if importance >= 4 else "Medium" if importance >= 3 else "Standard"}"""
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:
record = {
'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')
}
self.email_history.append(record)
return True
except:
return False
def get_email_history_text(self) -> str:
"""이메일 발송 이력 반환"""
if not self.email_history:
return """📧 **이메일 발송 현황**
아직 발송된 이메일이 없습니다.
**사용 방법:**
1. 기업 분석 실행
2. 📧 이메일 탭에서 내용 확인/수정
3. '실제 이메일 발송' 버튼 클릭
4. 이메일이 jeongkee10@gmission.com으로 발송됩니다"""
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['subject'][:50]}...
- 상태: ✅ {record['status']}
---
"""
return history_text
# ==================== 데이터 처리 시스템 ====================
class OptimizedDataProcessor:
@staticmethod
def find_column_mapping(df_columns: List[str]) -> Dict[str, str]:
"""컬럼 매핑 찾기"""
mapping = {}
for standard_name, possible_names in EXACT_COLUMN_MAPPING.items():
for possible_name in possible_names:
if possible_name in df_columns:
mapping[standard_name] = possible_name
break
return mapping
@staticmethod
def safe_numeric_conversion(series: pd.Series) -> pd.Series:
"""안전한 숫자 변환"""
try:
processed = series.astype(str).copy()
processed = processed.replace(['정보 없음', 'nan', 'null', 'NaN', '', 'N/A'], '0')
grade_mapping = {'S': '5', 'A': '4', 'B': '3', 'C': '2', 'D': '1'}
processed = processed.replace(grade_mapping)
processed = processed.str.replace(r'[^\d.]', '', regex=True).replace('', '0')
return pd.to_numeric(processed, errors='coerce').fillna(0)
except:
return pd.Series([0] * len(series))
@staticmethod
def process_data(df: pd.DataFrame) -> pd.DataFrame:
"""데이터 처리 메인 함수"""
try:
if df is None or df.empty:
return pd.DataFrame()
column_mapping = OptimizedDataProcessor.find_column_mapping(df.columns.tolist())
processed_df = pd.DataFrame()
for standard_name, original_name in column_mapping.items():
if original_name in df.columns:
processed_df[standard_name] = df[original_name].copy()
# 필수 컬럼 보장
if '기업명' not in processed_df.columns:
processed_df['기업명'] = df.iloc[:, 1] if len(df.columns) > 1 else [f"기업_{i+1}" for i in range(len(df))]
if 'Ecosystem_Category' not in processed_df.columns:
processed_df['Ecosystem_Category'] = '미분류'
# 중요도 처리
if '중요도' in processed_df.columns:
processed_df['중요도'] = OptimizedDataProcessor.safe_numeric_conversion(processed_df['중요도'])
processed_df['중요도등급'] = processed_df['중요도'].apply(
lambda x: 'S급 (최우선)' if x == 5 else
'A급 (핵심)' if x == 4 else
'B급 (유망)' if x == 3 else
'C급 (관심)' if x == 2 else
'D급 (참고)' if x == 1 else '미분류'
)
processed_df = processed_df.fillna('정보 없음')
# 메모리 최적화
for col in processed_df.columns:
if processed_df[col].dtype == 'object':
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_ecosystem_optimized_charts(df: pd.DataFrame, ecosystem_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, "데이터가 없거나 Plotly를 사용할 수 없습니다."
# 데이터 필터링
if ecosystem_filter != "전체":
filtered_df = df[df['Ecosystem_Category'] == ecosystem_filter].copy()
filter_info = f"'{ecosystem_filter}' 생태계"
else:
filtered_df = df.copy()
filter_info = "전체 생태계"
if filtered_df.empty:
return None, None, None, None, f"{ecosystem_filter} 카테고리에 기업이 없습니다."
# 요약 정보 생성
total_companies = len(filtered_df)
s_grade = len(filtered_df[filtered_df['중요도'] == 5]) if '중요도' in filtered_df.columns else 0
a_grade = len(filtered_df[filtered_df['중요도'] == 4]) if '중요도' in filtered_df.columns else 0
countries = filtered_df['HQ_국가'].nunique() if 'HQ_국가' in filtered_df.columns else 0
# 생태계별 특화 분석
ecosystem_focus = ECOSYSTEM_OPTIMIZED_COLUMNS.get(ecosystem_filter, {}).get('focus', 'comprehensive analysis')
summary_text = f"""### 📊 {filter_info} 분석 요약
**총 기업 수:** {total_companies:,}
**S급 최우선 기업:** {s_grade}
**A급 핵심 기업:** {a_grade}
**참여 국가:** {countries}개국
**분석 포커스:** {ecosystem_focus}
**전략적 기회:** {s_grade + a_grade}개 핵심 기업이 전체의 {((s_grade + a_grade) / total_companies * 100):.1f}%"""
# 1. 생태계별 파스텔톤 분포 차트
if ecosystem_filter == "전체":
ecosystem_counts = filtered_df['Ecosystem_Category'].value_counts()
ecosystem_colors = [PASTEL_COLORS['ecosystem_colors'].get(eco, PASTEL_COLORS['primary_palette'][i % 7])
for i, eco in enumerate(ecosystem_counts.index)]
fig1 = px.pie(
values=ecosystem_counts.values,
names=ecosystem_counts.index,
title=f"🏗️ 생태계별 기업 분포 (총 {total_companies}개)",
color_discrete_sequence=ecosystem_colors
)
fig1.update_traces(textposition='inside', textinfo='percent+label',
textfont=dict(size=12, color='white'))
else:
fig1 = go.Figure(data=[go.Pie(
labels=[ecosystem_filter],
values=[total_companies],
marker_colors=[PASTEL_COLORS['ecosystem_colors'].get(ecosystem_filter, '#E5BAFF')],
textinfo='value+label',
textfont=dict(size=14, color='white')
)])
fig1.update_layout(title=f"🏗️ {ecosystem_filter} 생태계 ({total_companies}개 기업)")
fig1.update_layout(
font=dict(size=14),
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)',
height=400
)
# 2. 중요도별 파스텔톤 분포 차트
if '중요도' in filtered_df.columns:
importance_counts = filtered_df['중요도'].value_counts().sort_index(ascending=False)
grade_names = {5: 'S급', 4: 'A급', 3: 'B급', 2: 'C급', 1: 'D급'}
if not importance_counts.empty:
x_labels = [grade_names.get(grade, f'{grade}급') for grade in importance_counts.index]
y_values = importance_counts.values
bar_colors = [PASTEL_COLORS['importance_colors'].get(grade, '#E5E5E5')
for grade in importance_counts.index]
fig2 = go.Figure(data=[
go.Bar(x=x_labels, y=y_values,
marker_color=bar_colors,
text=y_values, textposition='auto',
textfont=dict(size=12, color='black'))
])
fig2.update_layout(
title=f"⭐ {filter_info} 중요도별 분포",
xaxis_title="중요도 등급",
yaxis_title="기업 수",
font=dict(size=14),
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)',
height=400
)
else:
fig2 = go.Figure()
else:
fig2 = go.Figure()
# 3. 생태계별 최적화된 추가 차트
fig3 = PastelChartGenerator.create_ecosystem_specific_chart(filtered_df, ecosystem_filter)
# 4. 생태계별 상세 분석 차트
fig4 = PastelChartGenerator.create_detailed_analysis_chart(filtered_df, ecosystem_filter)
return fig1, fig2, fig3, fig4, summary_text
except Exception as e:
print(f"차트 생성 오류: {e}")
return None, None, None, None, f"차트 생성 중 오류: {str(e)}"
@staticmethod
def create_ecosystem_specific_chart(df: pd.DataFrame, ecosystem_filter: str) -> Any:
"""생태계별 특화 차트 생성"""
try:
if ecosystem_filter == "Infra":
# 인프라: LLM 배포 방식 분포
if 'LLM_Deployment' in df.columns:
deployment_counts = df['LLM_Deployment'].value_counts().head(8)
if not deployment_counts.empty:
fig = px.bar(
x=deployment_counts.index,
y=deployment_counts.values,
title="🚀 LLM 배포 방식 분포",
color_discrete_sequence=PASTEL_COLORS['primary_palette']
)
fig.update_layout(xaxis_title="배포 방식", yaxis_title="기업 수")
return fig
elif ecosystem_filter == "Foundation Model":
# 파운데이션 모델: 오픈소스 여부 분포
if 'OpenSource여부' in df.columns:
opensource_counts = df['OpenSource여부'].value_counts()
if not opensource_counts.empty:
fig = px.pie(
values=opensource_counts.values,
names=opensource_counts.index,
title="🔓 오픈소스 여부 분포",
color_discrete_sequence=PASTEL_COLORS['secondary_palette']
)
return fig
elif ecosystem_filter == "Platform / Agentic AI Pipeline":
# 플랫폼: 응용 도메인 분포
if 'Application_Domain' in df.columns:
domain_counts = df['Application_Domain'].value_counts().head(10)
if not domain_counts.empty:
fig = px.bar(
x=domain_counts.values,
y=domain_counts.index,
orientation='h',
title="🎯 응용 도메인별 분포",
color_discrete_sequence=PASTEL_COLORS['primary_palette']
)
fig.update_layout(xaxis_title="기업 수", yaxis_title="응용 도메인")
return fig
elif ecosystem_filter == "Application":
# 애플리케이션: 타겟 고객 분포
if 'TargetCustomer' in df.columns:
customer_counts = df['TargetCustomer'].value_counts().head(8)
if not customer_counts.empty:
fig = px.bar(
x=customer_counts.index,
y=customer_counts.values,
title="👥 타겟 고객별 분포",
color_discrete_sequence=PASTEL_COLORS['primary_palette']
)
fig.update_layout(xaxis_title="타겟 고객", yaxis_title="기업 수")
return fig
else:
# 전체: 국가별 분포
if 'HQ_국가' in df.columns:
country_counts = df['HQ_국가'].value_counts().head(10)
if not country_counts.empty:
fig = px.bar(
x=country_counts.values,
y=country_counts.index,
orientation='h',
title="🌍 주요 국가별 기업 수",
color_discrete_sequence=PASTEL_COLORS['primary_palette']
)
fig.update_layout(xaxis_title="기업 수", yaxis_title="국가")
return fig
return go.Figure()
except Exception as e:
print(f"생태계별 차트 생성 오류: {e}")
return go.Figure()
@staticmethod
def create_detailed_analysis_chart(df: pd.DataFrame, ecosystem_filter: str) -> Any:
"""상세 분석 차트 생성"""
try:
if ecosystem_filter == "Infra":
# 인프라: 설립연도별 분포
if '설립연도' in df.columns:
years = df['설립연도'][df['설립연도'] > 0]
if not years.empty:
fig = px.histogram(
x=years,
nbins=20,
title="📅 설립연도별 분포",
color_discrete_sequence=[PASTEL_COLORS['ecosystem_colors']['Infra']]
)
fig.update_layout(xaxis_title="설립연도", yaxis_title="기업 수")
return fig
elif ecosystem_filter == "Foundation Model":
# 파운데이션 모델: PrimaryLLM 분포
if 'PrimaryLLM' in df.columns:
llm_counts = df['PrimaryLLM'].value_counts().head(8)
if not llm_counts.empty:
fig = px.bar(
x=llm_counts.index,
y=llm_counts.values,
title="🤖 주요 LLM 모델 분포",
color_discrete_sequence=PASTEL_COLORS['primary_palette']
)
fig.update_layout(xaxis_title="LLM 모델", yaxis_title="기업 수")
return fig
elif ecosystem_filter == "Platform / Agentic AI Pipeline":
# 플랫폼: STT/TTS 지원 현황
if 'STT_TTS지원' in df.columns:
stt_counts = df['STT_TTS지원'].value_counts()
if not stt_counts.empty:
fig = px.pie(
values=stt_counts.values,
names=stt_counts.index,
title="🎤 STT/TTS 지원 현황",
color_discrete_sequence=PASTEL_COLORS['secondary_palette']
)
return fig
elif ecosystem_filter == "Application":
# 애플리케이션: 한국 지원 현황
if 'KoreaSupport' in df.columns:
korea_counts = df['KoreaSupport'].value_counts()
if not korea_counts.empty:
fig = px.pie(
values=korea_counts.values,
names=korea_counts.index,
title="🇰🇷 한국 시장 지원 현황",
color_discrete_sequence=PASTEL_COLORS['primary_palette']
)
return fig
else:
# 전체: 중요도와 설립연도 상관관계
if '중요도' in df.columns and '설립연도' in df.columns:
valid_data = df[(df['중요도'] > 0) & (df['설립연도'] > 0)]
if not valid_data.empty:
fig = px.scatter(
valid_data,
x='설립연도',
y='중요도',
size=[10] * len(valid_data),
title="📊 설립연도 vs 중요도 분포",
color_discrete_sequence=[PASTEL_COLORS['ecosystem_colors']['전체']]
)
fig.update_layout(xaxis_title="설립연도", yaxis_title="중요도")
return fig
return go.Figure()
except Exception as e:
print(f"상세 분석 차트 생성 오류: {e}")
return go.Figure()
# ==================== 기업 분석 시스템 ====================
def analyze_company_comprehensive(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('기업명')
ecosystem_cat = safe_get('Ecosystem_Category')
importance = safe_get('중요도', 0)
importance_grade = safe_get('중요도등급', '미분류')
email_address = safe_get('이메일')
country = safe_get('HQ_국가')
website = safe_get('웹사이트')
# 생태계별 최적화된 분석
optimized_info = ECOSYSTEM_OPTIMIZED_COLUMNS.get(ecosystem_cat, {})
primary_columns = optimized_info.get('primary', [])
focus_area = optimized_info.get('focus', 'comprehensive business analysis')
# 액션 추천
action_recommendations = {
5: "🚀 **최우선 협력 추진** - S급 전략적 파트너로 즉시 파트너십 개시",
4: "🎯 **핵심 파트너십 개발** - A급 중요 기업으로 상세 협력 방안 수립",
3: "🤝 **전술적 협력 검토** - B급 유망 파트너로 단계적 접근",
2: "👀 **시장 동향 모니터링** - C급 관심 기업으로 지속 관찰",
1: "📋 **정보 수집 및 분석** - D급 참고 기업으로 기본 정보 축적"
}
try:
importance_num = float(importance)
action_recommendation = action_recommendations.get(int(importance_num), "📋 **기본 정보 수집**")
except:
action_recommendation = "📋 **기본 정보 수집**"
# 생태계별 상세 분석 생성
ecosystem_analysis = generate_ecosystem_specific_analysis(data, ecosystem_cat, primary_columns)
# 최종 분석 결과
analysis = f"""# 🏢 {company_name_clean} 종합 분석
## ✅ 생태계별 최적화 분석 완료!
## ⭐ 전략적 평가
- **전략적 중요도:** {importance}/5점 ({importance_grade})
- **생태계 분류:** {ecosystem_cat}
- **분석 포커스:** {focus_area}
- **권장 액션:** {action_recommendation}
---
## 📍 기업 기본 정보
- **본사 위치:** {country} - {safe_get('HQ_도시')}
- **설립 연도:** {safe_get('설립연도')}
- **창업자:** {safe_get('창업자')}
- **주요 투자자:** {safe_get('주요투자자')}
## 🎯 기업 개요
{safe_get('기업개요')}
{ecosystem_analysis}
## 🔗 연락처 및 웹 정보
- **공식 웹사이트:** {website}
- **문의 페이지:** {safe_get('문의URL')}
- **이메일:** {email_address}
- **전화번호:** {safe_get('전화번호')}
---
## 💡 맞춤형 파트너십 전략
{generate_partnership_strategy(ecosystem_cat, int(importance) if str(importance).isdigit() else 3)}
---
**✨ 이메일 탭으로 이동하면 자동 생성된 맞춤형 파트너십 이메일을 확인할 수 있습니다!**
**📧 '실제 이메일 발송' 버튼으로 jeongkee10@gmission.com에 이메일을 발송할 수 있습니다.**"""
# 이메일 자동 생성
if email_address != '정보 없음' and '@' in email_address:
subject, body, recipient = email_system.create_partnership_email(
company_name_clean, email_address, ecosystem_cat, int(importance) if str(importance).isdigit() else 3, country, website
)
return analysis, subject, body, recipient
else:
return analysis, "", "", ""
except Exception as e:
error_msg = f"기업 분석 오류: {str(e)}"
print(error_msg)
return error_msg, "", "", ""
def generate_ecosystem_specific_analysis(data, ecosystem_cat: str, primary_columns: List[str]) -> str:
"""생태계별 특화 분석 생성"""
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)
if ecosystem_cat == "Infra":
return f"""
## 🏗️ 인프라 생태계 전문 분석
- **주요 LLM:** {safe_get('PrimaryLLM')}
- **배포 방식:** {safe_get('LLM_Deployment')}
- **주요 제품/서비스:** {safe_get('주요제품')}
- **파트너십 프로그램:** {safe_get('PartnershipProgram')}
**인프라 특화 협력 방안:**
• GPU/클라우드 리소스 공동 활용 및 최적화
• AI 워크로드 배포 및 스케일링 기술 협력
• 한국 데이터센터 및 엣지 컴퓨팅 구축
• 고성능 AI 인프라 공동 개발"""
elif ecosystem_cat == "Foundation Model":
return f"""
## 🤖 파운데이션 모델 생태계 전문 분석
- **주요 LLM:** {safe_get('PrimaryLLM')}
- **오픈소스 여부:** {safe_get('OpenSource여부')}
- **배포 방식:** {safe_get('LLM_Deployment')}
- **타겟 고객:** {safe_get('TargetCustomer')}
**파운데이션 모델 특화 협력 방안:**
• 한국어 특화 모델 공동 개발 및 Fine-tuning
• 모델 라이센싱 및 기술 이전 협력
• 멀티모달 AI 모델 공동 연구
• 오픈소스 생태계 기여 및 협력"""
elif ecosystem_cat == "Platform / Agentic AI Pipeline":
return f"""
## 🔗 플랫폼/에이전틱 AI 파이프라인 전문 분석
- **응용 도메인:** {safe_get('Application_Domain')}
- **STT/TTS 지원:** {safe_get('STT_TTS지원')}
- **타겟 고객:** {safe_get('TargetCustomer')}
- **가격 정책:** {safe_get('PricingModel')}
**플랫폼 특화 협력 방안:**
• API 연동 및 상호 운용성 확보
• 에이전틱 워크플로우 자동화 플랫폼 구축
• 엔터프라이즈 AI 오케스트레이션 솔루션
• 통합 플랫폼 생태계 공동 개발"""
elif ecosystem_cat == "Application":
return f"""
## 📱 애플리케이션 생태계 전문 분석
- **응용 도메인:** {safe_get('Application_Domain')}
- **타겟 고객:** {safe_get('TargetCustomer')}
- **가격 정책:** {safe_get('PricingModel')}
- **한국 지원:** {safe_get('KoreaSupport')}
**애플리케이션 특화 협력 방안:**
• 산업별 맞춤형 AI 솔루션 공동 개발
• 한국 시장 진출 및 현지화 협력
• B2B/B2C 고객 사례 공유 및 공동 마케팅
• 실제 비즈니스 적용 사례 구축"""
else:
return f"""
## 🔍 종합 기술 분석
- **주요 기술:** {safe_get('주요제품')}
- **응용 분야:** {safe_get('Application_Domain')}
- **시장 접근:** {safe_get('TargetCustomer')}
**종합 협력 방안:**
• 기술 통합 및 공동 개발
• 시장 확장 전략 협력
• 혁신 생태계 구축"""
def generate_partnership_strategy(ecosystem_cat: str, importance: int) -> str:
"""파트너십 전략 생성"""
strategies = {
"Infra": """
**인프라 파트너십 전략:**
1. **기술 협력**: 클라우드 인프라 공동 구축 및 최적화
2. **시장 진출**: 한국/APAC 데이터센터 확장 협력
3. **투자 협력**: 인프라 투자 펀드 공동 조성
4. **표준화**: AI 인프라 표준 개발 공동 참여""",
"Foundation Model": """
**파운데이션 모델 파트너십 전략:**
1. **기술 협력**: 한국어 LLM 공동 개발 및 최적화
2. **라이센싱**: 모델 기술 이전 및 라이센싱 협력
3. **연구 협력**: 공동 연구소 설립 및 인재 교류
4. **생태계**: 오픈소스 기여 및 커뮤니티 구축""",
"Platform / Agentic AI Pipeline": """
**플랫폼 파트너십 전략:**
1. **통합 협력**: API 상호 연동 및 플랫폼 통합
2. **솔루션**: 엔터프라이즈 AI 솔루션 공동 개발
3. **마케팅**: 공동 고객 확보 및 시장 개척
4. **표준화**: 에이전틱 AI 표준 및 프로토콜 개발""",
"Application": """
**애플리케이션 파트너십 전략:**
1. **현지화**: 한국 시장 맞춤형 애플리케이션 개발
2. **고객 확보**: 공동 세일즈 및 마케팅 협력
3. **사례 구축**: 성공 사례 공동 개발 및 홍보
4. **투자**: 후속 투자 및 사업 확장 협력"""
}
base_strategy = strategies.get(ecosystem_cat, "**종합 파트너십 전략:** 기술, 시장, 투자 전방위 협력")
if importance >= 4:
priority = "\n\n**🚀 최우선 추진 권장:** 즉시 파트너십 개시 및 전담팀 구성"
elif importance >= 3:
priority = "\n\n**🎯 중점 검토 권장:** 단계별 협력 방안 수립 및 파일럿 프로젝트 진행"
else:
priority = "\n\n**👀 지속 모니터링:** 시장 동향 관찰 및 기회 발굴"
return base_strategy + priority
# ==================== 파일 처리 ====================
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)
success_msg = f"✅ 파일 처리 완료!\n- 총 기업 수: {len(processed_df):,}개\n- 컬럼 수: {len(processed_df.columns)}개"
# 메모리 최적화
del df
gc.collect()
return processed_df, success_msg
except Exception as e:
return None, f"파일 처리 오류: {str(e)}"
# ==================== 메인 대시보드 ====================
def create_huggingface_dashboard():
"""허깅페이스 배포용 메인 대시보드"""
# Gmail 이메일 시스템 초기화
email_system = GmailEmailSystem()
# 커스텀 CSS
custom_css = """
.gradio-container {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
max-width: 1400px !important;
margin: 0 auto !important;
}
.ecosystem-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 10px;
padding: 20px;
margin: 10px;
color: white;
}
.pastel-btn {
background: linear-gradient(135deg, #FFB3BA 0%, #FFDFBA 100%);
border: none;
border-radius: 8px;
color: #333;
font-weight: bold;
}
"""
with gr.Blocks(
theme=gr.themes.Soft(primary_hue="blue", secondary_hue="cyan"),
title="G-Mission Agentic AI Partner Database",
css=custom_css
) as demo:
# 헤더
gr.Markdown("""
# 🚀 G-Mission Agentic AI 글로벌 파트너 DB 분석 대시보드
### ✅ 허깅페이스 배포 완료 | ✅ Gmail SMTP 이메일 | ✅ 파스텔톤 차트 | ✅ 생태계별 최적화
**155개 글로벌 Agentic AI 기업 완벽 분석** | **실시간 파트너십 이메일 발송**
**주요 기능:**
- 🎨 **파스텔톤 시각화**: 생태계별 맞춤 색상으로 직관적 분석
- 🏗️ **생태계별 최적화**: Infra, Foundation Model, Platform, Application 특화 분석
- 📧 **Gmail SMTP 연동**: jeongkee10@gmission.com으로 실시간 이메일 발송
- 🎯 **맞춤형 파트너십**: 기업별/생태계별 최적화된 협력 제안
""")
# 상태 변수
df_state = gr.State(value=None)
# 파일 업로드
with gr.Row():
with gr.Column():
file_upload = gr.File(
label="📁 Agentic AI 기업 데이터베이스 업로드",
file_types=['.csv', '.xlsx', '.xls'],
file_count="single"
)
upload_status = gr.Markdown("📋 **현재 상태:** 파일을 업로드해주세요.")
# 메인 탭
with gr.Tabs():
# 탭 1: 파스텔톤 시장 분석
with gr.TabItem("🎨 파스텔톤 시장 분석"):
with gr.Row():
with gr.Column(scale=1):
market_ecosystem_filter = gr.Dropdown(
choices=ECOSYSTEM_CATEGORIES,
value="전체",
label="🏗️ 생태계 필터",
info="특정 생태계 선택 시 해당 영역에 최적화된 분석 제공"
)
market_analysis_btn = gr.Button(
"🎨 파스텔톤 분석 실행",
variant="primary",
size="lg"
)
market_summary = gr.Markdown(
"생태계를 선택하고 분석을 실행하면 맞춤형 요약이 표시됩니다.",
label="📊 분석 요약"
)
with gr.Row():
with gr.Column():
chart1 = gr.Plot(label="🏗️ 생태계별 분포")
with gr.Column():
chart2 = gr.Plot(label="⭐ 중요도별 분포")
with gr.Row():
with gr.Column():
chart3 = gr.Plot(label="🎯 생태계별 특화 분석")
with gr.Column():
chart4 = gr.Plot(label="📊 상세 분석")
# 탭 2: 생태계별 최적화 기업 분석
with gr.TabItem("🏢 생태계별 최적화 기업 분석"):
with gr.Row():
with gr.Column(scale=1):
ecosystem_filter_company = gr.Dropdown(
choices=ECOSYSTEM_CATEGORIES,
value="전체",
label="🏗️ 생태계별 필터링",
info="각 생태계에 최적화된 컬럼과 분석 제공"
)
company_dropdown = gr.Dropdown(
choices=[],
label="🏢 기업 선택",
info="생태계별 필터링 후 기업 선택"
)
analyze_company_btn = gr.Button(
"🔍 생태계별 최적화 분석 실행",
variant="primary",
size="lg"
)
# 생태계별 정보 표시
ecosystem_info = gr.Markdown(
"""### 🏗️ 생태계별 최적화 정보
**Infra**: LLM 배포, 인프라 기술, 파트너십 프로그램
**Foundation Model**: LLM 모델, 오픈소스, 기술 이전
**Platform**: 응용도메인, STT/TTS, 타겟고객, 가격정책
**Application**: 타겟고객, 가격정책, 한국지원, 애플리케이션""",
label="생태계별 특화 정보"
)
with gr.Column(scale=2):
company_analysis = gr.Markdown(
"""# 🏢 생태계별 최적화 기업 분석
생태계를 선택하고 기업을 선택한 후 분석을 실행하면:
**🎯 생태계별 특화 분석:**
- **Infra**: 인프라 기술, 배포 방식, 파트너십 중심 분석
- **Foundation Model**: LLM 모델, 오픈소스, 기술력 중심 분석
- **Platform**: 플랫폼 통합, API, 에이전틱 파이프라인 중심 분석
- **Application**: 실제 응용, 시장 접근, 고객 중심 분석
**✨ 각 생태계에 최적화된 맞춤형 파트너십 전략이 제공됩니다!**""",
label="기업 상세 분석"
)
# 탭 3: Gmail SMTP 이메일 시스템
with gr.TabItem("📧 Gmail SMTP 이메일"):
with gr.Row():
with gr.Column():
gr.Markdown("""## 📧 Gmail SMTP 이메일 시스템
**설정 상태:**
- 📧 Gmail 계정: 허깅페이스 Secrets에서 로드
- 🎯 수신 이메일: jeongkee10@gmission.com (고정)
- 🔐 보안: Gmail 앱 비밀번호 사용
**사용 방법:**
1. 기업 분석 실행 → 이메일 자동 생성
2. 아래에서 이메일 내용 확인/수정
3. '실제 이메일 발송' 버튼 클릭
4. jeongkee10@gmission.com으로 이메일 발송됨""")
# 이메일 필드들
email_recipient = gr.Textbox(
label="📧 대상 기업 이메일 (참조용)",
placeholder="기업 분석 후 자동 입력됩니다",
interactive=True
)
email_subject = gr.Textbox(
label="📧 이메일 제목 (수정 가능)",
placeholder="기업 분석 후 자동 생성됩니다",
interactive=True
)
email_body = gr.Textbox(
label="📧 이메일 내용 (수정 가능)",
lines=15,
placeholder="기업 분석 후 생태계별 맞춤형 이메일이 자동 생성됩니다",
interactive=True
)
# 실제 이메일 발송 버튼
real_email_send_btn = gr.Button(
"📧 실제 Gmail로 이메일 발송 (→ jeongkee10@gmission.com)",
variant="primary",
size="lg"
)
email_send_status = gr.Markdown(
"""### 📧 이메일 발송 안내
기업 분석을 실행하면 해당 기업에 맞는 맞춤형 파트너십 이메일이 자동 생성됩니다.
내용을 확인/수정한 후 '실제 Gmail로 이메일 발송' 버튼을 클릭하면
**jeongkee10@gmission.com**으로 이메일이 발송됩니다.
**발송되는 이메일에는:**
- 원본 대상 기업 정보
- 파트너십 제안 내용
- 다음 단계 가이드
- 대시보드 링크가 포함됩니다.""",
label="발송 상태"
)
# 탭 4: 이메일 발송 현황
with gr.TabItem("📧 이메일 발송 현황"):
with gr.Column():
refresh_email_btn = gr.Button(
"🔄 발송 현황 새로고침",
variant="primary"
)
email_history_display = gr.Markdown(
email_system.get_email_history_text(),
label="📧 이메일 발송 이력"
)
# ==================== 이벤트 핸들러 ====================
# 1. 파일 업로드
def handle_file_upload(file):
try:
df, status = process_uploaded_file(file)
if df is not None:
company_names = sorted(df['기업명'].unique().tolist())
return (
df,
status,
gr.update(choices=company_names, value=None)
)
else:
return None, status, gr.update(choices=[], value=None)
except Exception as e:
return None, f"파일 업로드 오류: {str(e)}", gr.update(choices=[], value=None)
file_upload.upload(
fn=handle_file_upload,
inputs=[file_upload],
outputs=[df_state, upload_status, company_dropdown]
)
# 2. 파스텔톤 시장 분석
def handle_market_analysis(df, ecosystem_filter):
try:
if df is None or df.empty:
return None, None, None, None, "데이터 파일을 업로드해주세요."
return PastelChartGenerator.create_ecosystem_optimized_charts(df, ecosystem_filter)
except Exception as e:
return None, None, None, None, f"시장 분석 오류: {str(e)}"
market_analysis_btn.click(
fn=handle_market_analysis,
inputs=[df_state, market_ecosystem_filter],
outputs=[chart1, chart2, chart3, chart4, market_summary]
)
# 3. 생태계별 기업 필터링
def update_company_dropdown(df, ecosystem_filter):
try:
if df is None or df.empty:
return gr.update(choices=[])
if ecosystem_filter != "전체":
filtered_df = df[df['Ecosystem_Category'] == ecosystem_filter]
else:
filtered_df = df
company_names = sorted(filtered_df['기업명'].unique().tolist())
return gr.update(choices=company_names, value=None)
except Exception as e:
return gr.update(choices=[])
ecosystem_filter_company.change(
fn=update_company_dropdown,
inputs=[df_state, ecosystem_filter_company],
outputs=[company_dropdown]
)
# 4. 🔥 핵심: 생태계별 최적화 기업 분석 + 이메일 자동 생성
analyze_company_btn.click(
fn=lambda df, company: analyze_company_comprehensive(df, company, email_system),
inputs=[df_state, company_dropdown],
outputs=[
company_analysis, # 기업 분석 결과
email_subject, # 이메일 제목 자동 입력
email_body, # 이메일 내용 자동 입력
email_recipient # 받는 사람 자동 입력
]
)
# 5. 실제 Gmail 이메일 발송
def handle_real_email_send(recipient, subject, body):
try:
if not recipient or '@' not in recipient:
return "올바른 대상 기업 이메일 주소가 필요합니다."
if not subject or not body:
return "이메일 제목과 내용을 입력해주세요."
# 회사명 추출
company_name = recipient.split('@')[0].capitalize()
# 실제 Gmail 발송
success, message = email_system.send_email(subject, body, company_name, recipient)
return message
except Exception as e:
return f"이메일 발송 처리 오류: {str(e)}"
real_email_send_btn.click(
fn=handle_real_email_send,
inputs=[email_recipient, email_subject, email_body],
outputs=[email_send_status]
)
# 6. 이메일 현황 새로고침
refresh_email_btn.click(
fn=lambda: email_system.get_email_history_text(),
outputs=[email_history_display]
)
return demo
# ==================== 메인 실행 ====================
def main():
"""메인 실행 함수"""
print("🚀 G-Mission 허깅페이스 배포용 Agentic AI 대시보드 시작!")
print("✅ Gmail SMTP 이메일 시스템")
print("✅ 파스텔톤 차트 색상")
print("✅ 생태계별 최적화")
print("✅ jeongkee10@gmission.com 수신")
try:
demo = create_huggingface_dashboard()
# 허깅페이스 배포용 launch 설정
demo.launch(
server_name="0.0.0.0",
server_port=7860,
debug=False,
share=False
)
print("✅ 허깅페이스 배포용 대시보드 실행 성공!")
except Exception as e:
print(f"❌ 대시보드 실행 오류: {e}")
traceback.print_exc()
if __name__ == "__main__":
main()