Spaces:
Build error
Build error
| """ | |
| Real-time Market Intelligence Dashboard for NAVADA | |
| Provides comprehensive market data, trends, and competitive intelligence | |
| """ | |
| import asyncio | |
| import json | |
| import logging | |
| import os | |
| from typing import Dict, Any, List, Optional | |
| from datetime import datetime, timedelta | |
| import yfinance as yf | |
| import pandas as pd | |
| import plotly.graph_objects as go | |
| import plotly.express as px | |
| from plotly.subplots import make_subplots | |
| import requests | |
| from openai import AsyncOpenAI | |
| logger = logging.getLogger(__name__) | |
| class MarketIntelligenceEngine: | |
| """Core engine for gathering and analyzing market intelligence data""" | |
| def __init__(self): | |
| self.client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY")) | |
| self.search_api_key = os.getenv("SEARCH_API_KEY") | |
| self.data_cache = {} | |
| self.cache_expiry = {} | |
| async def get_market_overview(self, sector: str = "technology") -> Dict[str, Any]: | |
| """Get comprehensive market overview for a specific sector""" | |
| try: | |
| cache_key = f"market_overview_{sector}" | |
| if self._is_cache_valid(cache_key): | |
| return self.data_cache[cache_key] | |
| # Get major sector ETFs and indices | |
| sector_symbols = self._get_sector_symbols(sector) | |
| market_data = {} | |
| for symbol, name in sector_symbols.items(): | |
| try: | |
| ticker = yf.Ticker(symbol) | |
| hist = ticker.history(period="1y") | |
| info = ticker.info | |
| if not hist.empty: | |
| current_price = hist['Close'].iloc[-1] | |
| ytd_change = ((current_price - hist['Close'].iloc[0]) / hist['Close'].iloc[0]) * 100 | |
| market_data[symbol] = { | |
| "name": name, | |
| "current_price": float(current_price), | |
| "ytd_change": float(ytd_change), | |
| "volume": float(hist['Volume'].iloc[-1]), | |
| "market_cap": info.get('totalAssets', 0), | |
| "52_week_high": float(hist['High'].max()), | |
| "52_week_low": float(hist['Low'].min()) | |
| } | |
| except Exception as e: | |
| logger.warning(f"Error fetching data for {symbol}: {e}") | |
| continue | |
| # Generate market insights using AI | |
| market_insights = await self._generate_market_insights(market_data, sector) | |
| result = { | |
| "sector": sector, | |
| "timestamp": datetime.now().isoformat(), | |
| "market_data": market_data, | |
| "insights": market_insights, | |
| "total_symbols": len(market_data) | |
| } | |
| self._cache_data(cache_key, result, hours=1) | |
| return result | |
| except Exception as e: | |
| logger.error(f"Error getting market overview: {e}") | |
| return {"status": "error", "message": str(e)} | |
| def _get_sector_symbols(self, sector: str) -> Dict[str, str]: | |
| """Get relevant ETF and index symbols for a sector""" | |
| sector_mappings = { | |
| "technology": { | |
| "QQQ": "NASDAQ-100 Technology", | |
| "XLK": "Technology Select Sector SPDR", | |
| "VGT": "Vanguard Information Technology ETF", | |
| "SOXX": "iShares Semiconductor ETF", | |
| "ARKK": "ARK Innovation ETF" | |
| }, | |
| "healthcare": { | |
| "XLV": "Health Care Select Sector SPDR", | |
| "VHT": "Vanguard Health Care ETF", | |
| "IBB": "iShares Biotechnology ETF", | |
| "XBI": "SPDR Biotech ETF" | |
| }, | |
| "finance": { | |
| "XLF": "Financial Select Sector SPDR", | |
| "VFH": "Vanguard Financials ETF", | |
| "KBE": "SPDR Banking ETF", | |
| "KRE": "SPDR Regional Banking ETF" | |
| }, | |
| "energy": { | |
| "XLE": "Energy Select Sector SPDR", | |
| "VDE": "Vanguard Energy ETF", | |
| "XOP": "SPDR Oil & Gas Exploration ETF", | |
| "ICLN": "iShares Clean Energy ETF" | |
| }, | |
| "retail": { | |
| "XRT": "SPDR Retail ETF", | |
| "RTH": "VanEck Retail ETF", | |
| "ONLN": "ProShares Online Retail ETF", | |
| "XLY": "Consumer Discretionary SPDR" | |
| } | |
| } | |
| return sector_mappings.get(sector, sector_mappings["technology"]) | |
| async def _generate_market_insights(self, market_data: Dict, sector: str) -> str: | |
| """Generate AI-powered insights from market data""" | |
| try: | |
| system_prompt = f""" | |
| You are a senior market analyst providing insights on the {sector} sector. | |
| Analyze the provided market data and generate 3-4 key insights covering: | |
| 1. Overall sector performance and trends | |
| 2. Notable winners and losers | |
| 3. Market sentiment and outlook | |
| 4. Potential opportunities or risks | |
| Keep insights concise, data-driven, and actionable for startup founders. | |
| """ | |
| user_prompt = f""" | |
| Sector: {sector} | |
| Market Data Summary: | |
| {json.dumps(market_data, indent=2)} | |
| Provide professional market analysis with specific insights for startup founders in this sector. | |
| """ | |
| response = await self.client.chat.completions.create( | |
| model="gpt-4", | |
| messages=[ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_prompt} | |
| ], | |
| max_tokens=400, | |
| temperature=0.3 | |
| ) | |
| return response.choices[0].message.content | |
| except Exception as e: | |
| logger.error(f"Error generating market insights: {e}") | |
| return "Market insights temporarily unavailable." | |
| async def get_competitive_landscape(self, company_name: str, industry: str) -> Dict[str, Any]: | |
| """Analyze competitive landscape for a specific company/industry""" | |
| try: | |
| cache_key = f"competitive_{company_name}_{industry}" | |
| if self._is_cache_valid(cache_key): | |
| return self.data_cache[cache_key] | |
| # Get industry leaders and competitors | |
| competitors = await self._identify_competitors(company_name, industry) | |
| competitive_data = {} | |
| for competitor in competitors[:10]: # Limit to top 10 | |
| try: | |
| ticker = yf.Ticker(competitor["symbol"]) | |
| info = ticker.info | |
| hist = ticker.history(period="1y") | |
| if not hist.empty: | |
| competitive_data[competitor["symbol"]] = { | |
| "name": competitor["name"], | |
| "market_cap": info.get('marketCap', 0), | |
| "revenue": info.get('totalRevenue', 0), | |
| "employees": info.get('fullTimeEmployees', 0), | |
| "pe_ratio": info.get('trailingPE', 0), | |
| "profit_margin": info.get('profitMargins', 0), | |
| "revenue_growth": info.get('revenueGrowth', 0), | |
| "current_price": float(hist['Close'].iloc[-1]), | |
| "ytd_performance": ((hist['Close'].iloc[-1] - hist['Close'].iloc[0]) / hist['Close'].iloc[0]) * 100 | |
| } | |
| except Exception as e: | |
| logger.warning(f"Error fetching competitor data for {competitor}: {e}") | |
| continue | |
| # Generate competitive analysis | |
| analysis = await self._generate_competitive_analysis(competitive_data, company_name, industry) | |
| result = { | |
| "company": company_name, | |
| "industry": industry, | |
| "timestamp": datetime.now().isoformat(), | |
| "competitors": competitive_data, | |
| "analysis": analysis, | |
| "market_leaders": self._identify_market_leaders(competitive_data) | |
| } | |
| self._cache_data(cache_key, result, hours=6) | |
| return result | |
| except Exception as e: | |
| logger.error(f"Error getting competitive landscape: {e}") | |
| return {"status": "error", "message": str(e)} | |
| async def _identify_competitors(self, company_name: str, industry: str) -> List[Dict[str, str]]: | |
| """Identify key competitors using AI and market data""" | |
| try: | |
| # Use AI to identify competitors | |
| system_prompt = """ | |
| You are a market research analyst. Identify the top public competitors for the given company and industry. | |
| Return a JSON list of competitors with their stock symbols and full company names. | |
| Format: [{"symbol": "AAPL", "name": "Apple Inc."}, ...] | |
| Focus on direct competitors that are publicly traded. | |
| """ | |
| user_prompt = f""" | |
| Company: {company_name} | |
| Industry: {industry} | |
| Identify 8-12 key public competitors with their stock symbols. | |
| """ | |
| response = await self.client.chat.completions.create( | |
| model="gpt-4", | |
| messages=[ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_prompt} | |
| ], | |
| max_tokens=300, | |
| temperature=0.3 | |
| ) | |
| try: | |
| competitors = json.loads(response.choices[0].message.content) | |
| return competitors if isinstance(competitors, list) else [] | |
| except json.JSONDecodeError: | |
| logger.warning("Failed to parse competitors JSON") | |
| return [] | |
| except Exception as e: | |
| logger.error(f"Error identifying competitors: {e}") | |
| return [] | |
| async def _generate_competitive_analysis(self, competitive_data: Dict, company_name: str, industry: str) -> str: | |
| """Generate AI-powered competitive analysis""" | |
| try: | |
| system_prompt = f""" | |
| You are a senior business analyst providing competitive intelligence for {company_name} in the {industry} industry. | |
| Analyze the competitive landscape and provide insights on: | |
| 1. Market positioning and differentiation opportunities | |
| 2. Financial performance benchmarks | |
| 3. Strategic threats and opportunities | |
| 4. Market dynamics and trends | |
| Be specific and actionable for startup strategy. | |
| """ | |
| user_prompt = f""" | |
| Company: {company_name} | |
| Industry: {industry} | |
| Competitive Data: | |
| {json.dumps(competitive_data, indent=2)} | |
| Provide strategic competitive analysis with actionable insights. | |
| """ | |
| response = await self.client.chat.completions.create( | |
| model="gpt-4", | |
| messages=[ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_prompt} | |
| ], | |
| max_tokens=500, | |
| temperature=0.3 | |
| ) | |
| return response.choices[0].message.content | |
| except Exception as e: | |
| logger.error(f"Error generating competitive analysis: {e}") | |
| return "Competitive analysis temporarily unavailable." | |
| def _identify_market_leaders(self, competitive_data: Dict) -> List[Dict[str, Any]]: | |
| """Identify market leaders based on key metrics""" | |
| try: | |
| if not competitive_data: | |
| return [] | |
| # Sort by market cap and revenue | |
| sorted_by_market_cap = sorted( | |
| competitive_data.items(), | |
| key=lambda x: x[1].get('market_cap', 0), | |
| reverse=True | |
| ) | |
| leaders = [] | |
| for symbol, data in sorted_by_market_cap[:5]: | |
| leaders.append({ | |
| "symbol": symbol, | |
| "name": data.get('name', symbol), | |
| "market_cap": data.get('market_cap', 0), | |
| "revenue": data.get('revenue', 0), | |
| "market_position": "Leader" if len(leaders) == 0 else "Major Player" | |
| }) | |
| return leaders | |
| except Exception as e: | |
| logger.error(f"Error identifying market leaders: {e}") | |
| return [] | |
| async def get_trend_analysis(self, keywords: List[str], timeframe: str = "1y") -> Dict[str, Any]: | |
| """Analyze market trends for specific keywords/topics""" | |
| try: | |
| cache_key = f"trends_{'_'.join(keywords)}_{timeframe}" | |
| if self._is_cache_valid(cache_key): | |
| return self.data_cache[cache_key] | |
| # Analyze related stocks and ETFs | |
| trend_data = {} | |
| for keyword in keywords: | |
| related_symbols = await self._find_related_symbols(keyword) | |
| keyword_performance = {} | |
| for symbol in related_symbols[:5]: # Top 5 related symbols | |
| try: | |
| ticker = yf.Ticker(symbol) | |
| hist = ticker.history(period=timeframe) | |
| if not hist.empty: | |
| performance = ((hist['Close'].iloc[-1] - hist['Close'].iloc[0]) / hist['Close'].iloc[0]) * 100 | |
| keyword_performance[symbol] = { | |
| "performance": float(performance), | |
| "volatility": float(hist['Close'].std()), | |
| "volume_avg": float(hist['Volume'].mean()) | |
| } | |
| except Exception as e: | |
| logger.warning(f"Error analyzing {symbol}: {e}") | |
| continue | |
| trend_data[keyword] = keyword_performance | |
| # Generate trend insights | |
| insights = await self._generate_trend_insights(trend_data, keywords, timeframe) | |
| result = { | |
| "keywords": keywords, | |
| "timeframe": timeframe, | |
| "timestamp": datetime.now().isoformat(), | |
| "trend_data": trend_data, | |
| "insights": insights | |
| } | |
| self._cache_data(cache_key, result, hours=2) | |
| return result | |
| except Exception as e: | |
| logger.error(f"Error getting trend analysis: {e}") | |
| return {"status": "error", "message": str(e)} | |
| async def _find_related_symbols(self, keyword: str) -> List[str]: | |
| """Find stock symbols related to a keyword""" | |
| # This is a simplified implementation | |
| # In production, you might use more sophisticated symbol mapping | |
| keyword_mappings = { | |
| "ai": ["NVDA", "GOOGL", "MSFT", "AMD", "INTC"], | |
| "cloud": ["AMZN", "MSFT", "GOOGL", "CRM", "SNOW"], | |
| "fintech": ["SQ", "PYPL", "V", "MA", "COIN"], | |
| "biotech": ["GILD", "AMGN", "BIIB", "REGN", "VRTX"], | |
| "ev": ["TSLA", "F", "GM", "NIO", "RIVN"], | |
| "crypto": ["COIN", "MSTR", "RIOT", "MARA", "HOOD"] | |
| } | |
| return keyword_mappings.get(keyword.lower(), ["SPY"]) | |
| async def _generate_trend_insights(self, trend_data: Dict, keywords: List[str], timeframe: str) -> str: | |
| """Generate AI insights about market trends""" | |
| try: | |
| system_prompt = f""" | |
| You are a market trend analyst providing insights on emerging trends and market dynamics. | |
| Analyze the trend data for the keywords: {', '.join(keywords)} over {timeframe}. | |
| Provide insights on: | |
| 1. Overall trend momentum and direction | |
| 2. Investment themes and opportunities | |
| 3. Risk factors and market dynamics | |
| 4. Implications for startups in these sectors | |
| Keep insights concise and actionable. | |
| """ | |
| user_prompt = f""" | |
| Keywords: {keywords} | |
| Timeframe: {timeframe} | |
| Trend Performance Data: | |
| {json.dumps(trend_data, indent=2)} | |
| Provide trend analysis with startup implications. | |
| """ | |
| response = await self.client.chat.completions.create( | |
| model="gpt-4", | |
| messages=[ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_prompt} | |
| ], | |
| max_tokens=400, | |
| temperature=0.3 | |
| ) | |
| return response.choices[0].message.content | |
| except Exception as e: | |
| logger.error(f"Error generating trend insights: {e}") | |
| return "Trend insights temporarily unavailable." | |
| def _is_cache_valid(self, cache_key: str) -> bool: | |
| """Check if cached data is still valid""" | |
| if cache_key not in self.cache_expiry: | |
| return False | |
| return datetime.now() < self.cache_expiry[cache_key] | |
| def _cache_data(self, cache_key: str, data: Any, hours: int = 1): | |
| """Cache data with expiry""" | |
| self.data_cache[cache_key] = data | |
| self.cache_expiry[cache_key] = datetime.now() + timedelta(hours=hours) | |
| class DashboardVisualizer: | |
| """Creates interactive visualizations for market intelligence dashboard""" | |
| def __init__(self): | |
| self.theme_colors = { | |
| "primary": "#667eea", | |
| "secondary": "#764ba2", | |
| "success": "#4CAF50", | |
| "danger": "#f44336", | |
| "warning": "#ff9800", | |
| "info": "#2196F3" | |
| } | |
| def create_market_overview_chart(self, market_data: Dict[str, Any]) -> str: | |
| """Create market overview visualization""" | |
| try: | |
| symbols = list(market_data["market_data"].keys()) | |
| names = [data["name"] for data in market_data["market_data"].values()] | |
| ytd_changes = [data["ytd_change"] for data in market_data["market_data"].values()] | |
| # Create bar chart | |
| fig = go.Figure() | |
| colors = [self.theme_colors["success"] if change >= 0 else self.theme_colors["danger"] | |
| for change in ytd_changes] | |
| fig.add_trace(go.Bar( | |
| x=symbols, | |
| y=ytd_changes, | |
| text=[f"{change:.1f}%" for change in ytd_changes], | |
| textposition='auto', | |
| marker_color=colors, | |
| hovertemplate='<b>%{x}</b><br>YTD Change: %{y:.1f}%<extra></extra>' | |
| )) | |
| fig.update_layout( | |
| title=f"{market_data['sector'].title()} Sector Performance (YTD)", | |
| xaxis_title="ETFs/Indices", | |
| yaxis_title="YTD Change (%)", | |
| template="plotly_white", | |
| font=dict(family="Inter, sans-serif"), | |
| height=400 | |
| ) | |
| return fig.to_html(include_plotlyjs=True, div_id="market-overview-chart") | |
| except Exception as e: | |
| logger.error(f"Error creating market overview chart: {e}") | |
| return "<div>Error creating market overview chart</div>" | |
| def create_competitive_comparison(self, competitive_data: Dict[str, Any]) -> str: | |
| """Create competitive landscape comparison""" | |
| try: | |
| competitors = competitive_data["competitors"] | |
| if not competitors: | |
| return "<div>No competitive data available</div>" | |
| # Create subplot with multiple metrics | |
| fig = make_subplots( | |
| rows=2, cols=2, | |
| subplot_titles=('Market Cap', 'Revenue Growth', 'Profit Margin', 'P/E Ratio'), | |
| specs=[[{"secondary_y": False}, {"secondary_y": False}], | |
| [{"secondary_y": False}, {"secondary_y": False}]] | |
| ) | |
| symbols = list(competitors.keys()) | |
| market_caps = [competitors[s].get('market_cap', 0) / 1e9 for s in symbols] # In billions | |
| revenue_growth = [competitors[s].get('revenue_growth', 0) * 100 for s in symbols] | |
| profit_margins = [competitors[s].get('profit_margin', 0) * 100 for s in symbols] | |
| pe_ratios = [competitors[s].get('pe_ratio', 0) for s in symbols] | |
| # Market Cap | |
| fig.add_trace(go.Bar(x=symbols, y=market_caps, name="Market Cap (B)", | |
| marker_color=self.theme_colors["primary"]), row=1, col=1) | |
| # Revenue Growth | |
| fig.add_trace(go.Bar(x=symbols, y=revenue_growth, name="Revenue Growth (%)", | |
| marker_color=self.theme_colors["success"]), row=1, col=2) | |
| # Profit Margin | |
| fig.add_trace(go.Bar(x=symbols, y=profit_margins, name="Profit Margin (%)", | |
| marker_color=self.theme_colors["info"]), row=2, col=1) | |
| # P/E Ratio | |
| fig.add_trace(go.Bar(x=symbols, y=pe_ratios, name="P/E Ratio", | |
| marker_color=self.theme_colors["warning"]), row=2, col=2) | |
| fig.update_layout( | |
| title="Competitive Landscape Analysis", | |
| template="plotly_white", | |
| font=dict(family="Inter, sans-serif"), | |
| height=600, | |
| showlegend=False | |
| ) | |
| return fig.to_html(include_plotlyjs=True, div_id="competitive-chart") | |
| except Exception as e: | |
| logger.error(f"Error creating competitive comparison: {e}") | |
| return "<div>Error creating competitive comparison</div>" | |
| def create_trend_heatmap(self, trend_data: Dict[str, Any]) -> str: | |
| """Create trend analysis heatmap""" | |
| try: | |
| keywords = trend_data["keywords"] | |
| trends = trend_data["trend_data"] | |
| if not trends: | |
| return "<div>No trend data available</div>" | |
| # Prepare data for heatmap | |
| symbols = set() | |
| for keyword_data in trends.values(): | |
| symbols.update(keyword_data.keys()) | |
| symbols = list(symbols) | |
| performance_matrix = [] | |
| for keyword in keywords: | |
| row = [] | |
| for symbol in symbols: | |
| performance = trends.get(keyword, {}).get(symbol, {}).get('performance', 0) | |
| row.append(performance) | |
| performance_matrix.append(row) | |
| # Create heatmap | |
| fig = go.Figure(data=go.Heatmap( | |
| z=performance_matrix, | |
| x=symbols, | |
| y=keywords, | |
| colorscale='RdYlGn', | |
| text=[[f"{val:.1f}%" for val in row] for row in performance_matrix], | |
| texttemplate="%{text}", | |
| textfont={"size": 10}, | |
| hoverongaps=False | |
| )) | |
| fig.update_layout( | |
| title=f"Trend Performance Heatmap ({trend_data['timeframe']})", | |
| xaxis_title="Symbols", | |
| yaxis_title="Keywords", | |
| template="plotly_white", | |
| font=dict(family="Inter, sans-serif"), | |
| height=400 | |
| ) | |
| return fig.to_html(include_plotlyjs=True, div_id="trend-heatmap") | |
| except Exception as e: | |
| logger.error(f"Error creating trend heatmap: {e}") | |
| return "<div>Error creating trend heatmap</div>" | |
| class MarketIntelligenceDashboard: | |
| """Main dashboard class that orchestrates market intelligence features""" | |
| def __init__(self): | |
| self.engine = MarketIntelligenceEngine() | |
| self.visualizer = DashboardVisualizer() | |
| async def create_dashboard_interface(self) -> str: | |
| """Create the main dashboard HTML interface""" | |
| return """ | |
| <div id="market-intelligence-dashboard" class="dashboard-container"> | |
| <div class="dashboard-header"> | |
| <h1>📊 Market Intelligence Dashboard</h1> | |
| <p>Real-time market data, competitive intelligence, and trend analysis</p> | |
| </div> | |
| <div class="dashboard-controls"> | |
| <div class="control-group"> | |
| <label>Sector Analysis:</label> | |
| <select id="sector-select"> | |
| <option value="technology">Technology</option> | |
| <option value="healthcare">Healthcare</option> | |
| <option value="finance">Finance</option> | |
| <option value="energy">Energy</option> | |
| <option value="retail">Retail</option> | |
| </select> | |
| <button onclick="loadMarketOverview()" class="dashboard-btn">Analyze Sector</button> | |
| </div> | |
| <div class="control-group"> | |
| <label>Competitive Analysis:</label> | |
| <input type="text" id="company-input" placeholder="Company name" /> | |
| <input type="text" id="industry-input" placeholder="Industry" /> | |
| <button onclick="loadCompetitiveAnalysis()" class="dashboard-btn">Analyze Competition</button> | |
| </div> | |
| <div class="control-group"> | |
| <label>Trend Analysis:</label> | |
| <input type="text" id="keywords-input" placeholder="Keywords (comma-separated)" /> | |
| <select id="timeframe-select"> | |
| <option value="1y">1 Year</option> | |
| <option value="6mo">6 Months</option> | |
| <option value="3mo">3 Months</option> | |
| <option value="1mo">1 Month</option> | |
| </select> | |
| <button onclick="loadTrendAnalysis()" class="dashboard-btn">Analyze Trends</button> | |
| </div> | |
| </div> | |
| <div class="dashboard-content"> | |
| <div id="market-overview-section" class="dashboard-section"> | |
| <h2>Market Overview</h2> | |
| <div id="market-overview-chart"></div> | |
| <div id="market-insights" class="insights-panel"></div> | |
| </div> | |
| <div id="competitive-section" class="dashboard-section"> | |
| <h2>Competitive Landscape</h2> | |
| <div id="competitive-chart"></div> | |
| <div id="competitive-insights" class="insights-panel"></div> | |
| </div> | |
| <div id="trends-section" class="dashboard-section"> | |
| <h2>Market Trends</h2> | |
| <div id="trend-heatmap"></div> | |
| <div id="trend-insights" class="insights-panel"></div> | |
| </div> | |
| </div> | |
| <div class="dashboard-footer"> | |
| <p>Last updated: <span id="last-updated">Never</span></p> | |
| <button onclick="refreshAllData()" class="refresh-btn">🔄 Refresh All Data</button> | |
| </div> | |
| </div> | |
| <style> | |
| .dashboard-container { | |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); | |
| border-radius: 20px; | |
| padding: 30px; | |
| margin: 20px 0; | |
| font-family: 'Inter', sans-serif; | |
| } | |
| .dashboard-header { | |
| text-align: center; | |
| margin-bottom: 30px; | |
| } | |
| .dashboard-header h1 { | |
| color: #2d3748; | |
| margin-bottom: 10px; | |
| } | |
| .dashboard-controls { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
| gap: 20px; | |
| margin-bottom: 30px; | |
| padding: 20px; | |
| background: white; | |
| border-radius: 15px; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| } | |
| .control-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| } | |
| .control-group label { | |
| font-weight: 600; | |
| color: #4a5568; | |
| } | |
| .control-group input, | |
| .control-group select { | |
| padding: 10px; | |
| border: 2px solid #e2e8f0; | |
| border-radius: 8px; | |
| font-size: 14px; | |
| } | |
| .dashboard-btn { | |
| padding: 12px 20px; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| border: none; | |
| border-radius: 8px; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: opacity 0.3s ease; | |
| } | |
| .dashboard-btn:hover { | |
| opacity: 0.9; | |
| } | |
| .dashboard-content { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 30px; | |
| } | |
| .dashboard-section { | |
| background: white; | |
| border-radius: 15px; | |
| padding: 25px; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| } | |
| .dashboard-section h2 { | |
| color: #2d3748; | |
| margin-bottom: 20px; | |
| padding-bottom: 10px; | |
| border-bottom: 2px solid #e2e8f0; | |
| } | |
| .insights-panel { | |
| background: #f7fafc; | |
| border-radius: 10px; | |
| padding: 20px; | |
| margin-top: 20px; | |
| border-left: 4px solid #667eea; | |
| } | |
| .dashboard-footer { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-top: 30px; | |
| padding: 20px; | |
| background: rgba(255, 255, 255, 0.8); | |
| border-radius: 10px; | |
| } | |
| .refresh-btn { | |
| padding: 10px 20px; | |
| background: #4CAF50; | |
| color: white; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| font-weight: 600; | |
| } | |
| .loading { | |
| text-align: center; | |
| padding: 40px; | |
| color: #718096; | |
| } | |
| .error { | |
| background: #fed7d7; | |
| color: #c53030; | |
| padding: 15px; | |
| border-radius: 8px; | |
| margin: 10px 0; | |
| } | |
| </style> | |
| <script> | |
| async function loadMarketOverview() { | |
| const sector = document.getElementById('sector-select').value; | |
| const chartDiv = document.getElementById('market-overview-chart'); | |
| const insightsDiv = document.getElementById('market-insights'); | |
| chartDiv.innerHTML = '<div class="loading">Loading market overview...</div>'; | |
| insightsDiv.innerHTML = ''; | |
| try { | |
| const response = await sendDashboardRequest('market_overview', { sector }); | |
| if (response.status === 'success') { | |
| chartDiv.innerHTML = response.chart; | |
| insightsDiv.innerHTML = `<h3>Market Insights</h3><p>${response.insights}</p>`; | |
| updateLastUpdated(); | |
| } else { | |
| chartDiv.innerHTML = `<div class="error">Error: ${response.message}</div>`; | |
| } | |
| } catch (error) { | |
| chartDiv.innerHTML = `<div class="error">Error loading market data</div>`; | |
| } | |
| } | |
| async function loadCompetitiveAnalysis() { | |
| const company = document.getElementById('company-input').value; | |
| const industry = document.getElementById('industry-input').value; | |
| if (!company || !industry) { | |
| alert('Please enter both company name and industry'); | |
| return; | |
| } | |
| const chartDiv = document.getElementById('competitive-chart'); | |
| const insightsDiv = document.getElementById('competitive-insights'); | |
| chartDiv.innerHTML = '<div class="loading">Loading competitive analysis...</div>'; | |
| insightsDiv.innerHTML = ''; | |
| try { | |
| const response = await sendDashboardRequest('competitive_analysis', { company, industry }); | |
| if (response.status === 'success') { | |
| chartDiv.innerHTML = response.chart; | |
| insightsDiv.innerHTML = `<h3>Competitive Analysis</h3><p>${response.analysis}</p>`; | |
| updateLastUpdated(); | |
| } else { | |
| chartDiv.innerHTML = `<div class="error">Error: ${response.message}</div>`; | |
| } | |
| } catch (error) { | |
| chartDiv.innerHTML = `<div class="error">Error loading competitive data</div>`; | |
| } | |
| } | |
| async function loadTrendAnalysis() { | |
| const keywordsInput = document.getElementById('keywords-input').value; | |
| const timeframe = document.getElementById('timeframe-select').value; | |
| if (!keywordsInput) { | |
| alert('Please enter keywords for trend analysis'); | |
| return; | |
| } | |
| const keywords = keywordsInput.split(',').map(k => k.trim()); | |
| const chartDiv = document.getElementById('trend-heatmap'); | |
| const insightsDiv = document.getElementById('trend-insights'); | |
| chartDiv.innerHTML = '<div class="loading">Loading trend analysis...</div>'; | |
| insightsDiv.innerHTML = ''; | |
| try { | |
| const response = await sendDashboardRequest('trend_analysis', { keywords, timeframe }); | |
| if (response.status === 'success') { | |
| chartDiv.innerHTML = response.chart; | |
| insightsDiv.innerHTML = `<h3>Trend Insights</h3><p>${response.insights}</p>`; | |
| updateLastUpdated(); | |
| } else { | |
| chartDiv.innerHTML = `<div class="error">Error: ${response.message}</div>`; | |
| } | |
| } catch (error) { | |
| chartDiv.innerHTML = `<div class="error">Error loading trend data</div>`; | |
| } | |
| } | |
| async function refreshAllData() { | |
| await loadMarketOverview(); | |
| if (document.getElementById('company-input').value && document.getElementById('industry-input').value) { | |
| await loadCompetitiveAnalysis(); | |
| } | |
| if (document.getElementById('keywords-input').value) { | |
| await loadTrendAnalysis(); | |
| } | |
| } | |
| async function sendDashboardRequest(type, data) { | |
| if (window.chainlitAPI) { | |
| return await window.chainlitAPI.sendMessage({ | |
| type: 'dashboard_request', | |
| request_type: type, | |
| data: data | |
| }); | |
| } | |
| throw new Error('Chainlit API not available'); | |
| } | |
| function updateLastUpdated() { | |
| document.getElementById('last-updated').textContent = new Date().toLocaleString(); | |
| } | |
| // Auto-load technology sector overview on page load | |
| document.addEventListener('DOMContentLoaded', function() { | |
| setTimeout(loadMarketOverview, 1000); | |
| }); | |
| </script> | |
| """ | |
| async def handle_dashboard_request(self, request_type: str, data: Dict[str, Any]) -> Dict[str, Any]: | |
| """Handle different types of dashboard requests""" | |
| try: | |
| if request_type == "market_overview": | |
| sector = data.get("sector", "technology") | |
| market_data = await self.engine.get_market_overview(sector) | |
| if "status" in market_data and market_data["status"] == "error": | |
| return market_data | |
| chart_html = self.visualizer.create_market_overview_chart(market_data) | |
| return { | |
| "status": "success", | |
| "chart": chart_html, | |
| "insights": market_data["insights"], | |
| "data": market_data | |
| } | |
| elif request_type == "competitive_analysis": | |
| company = data.get("company", "") | |
| industry = data.get("industry", "") | |
| competitive_data = await self.engine.get_competitive_landscape(company, industry) | |
| if "status" in competitive_data and competitive_data["status"] == "error": | |
| return competitive_data | |
| chart_html = self.visualizer.create_competitive_comparison(competitive_data) | |
| return { | |
| "status": "success", | |
| "chart": chart_html, | |
| "analysis": competitive_data["analysis"], | |
| "data": competitive_data | |
| } | |
| elif request_type == "trend_analysis": | |
| keywords = data.get("keywords", []) | |
| timeframe = data.get("timeframe", "1y") | |
| trend_data = await self.engine.get_trend_analysis(keywords, timeframe) | |
| if "status" in trend_data and trend_data["status"] == "error": | |
| return trend_data | |
| chart_html = self.visualizer.create_trend_heatmap(trend_data) | |
| return { | |
| "status": "success", | |
| "chart": chart_html, | |
| "insights": trend_data["insights"], | |
| "data": trend_data | |
| } | |
| else: | |
| return {"status": "error", "message": "Unknown request type"} | |
| except Exception as e: | |
| logger.error(f"Error handling dashboard request: {e}") | |
| return {"status": "error", "message": str(e)} |