Added new features to the project
This commit is contained in:
169
lib/alwrity_ui/alwrity_researcher/README.md
Normal file
169
lib/alwrity_ui/alwrity_researcher/README.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# AI Web Researcher Dashboard for Content Creators
|
||||
|
||||
## Overview
|
||||
|
||||
The AI Web Researcher Dashboard is a comprehensive suite of research tools designed specifically for content creators. This dashboard integrates various web research modules to streamline the content research process, enhance content quality, and improve workflow efficiency.
|
||||
|
||||
## Available Research Modules
|
||||
|
||||
### 1. Search Engine Research Tools
|
||||
|
||||
#### Google SERP Search
|
||||
- **Functionality**: Retrieves organic search results, People Also Ask questions, and related searches
|
||||
- **Best for**: Initial topic research, understanding search intent, and identifying content gaps
|
||||
- **Key features**: Comprehensive search results with structured data extraction
|
||||
|
||||
#### Tavily AI Search
|
||||
- **Functionality**: Advanced AI-powered search with semantic understanding
|
||||
- **Best for**: In-depth research requiring contextual understanding
|
||||
- **Key features**: Provides direct answers to questions and follow-up question suggestions
|
||||
|
||||
### 2. Neural Search Tools
|
||||
|
||||
#### Metaphor Neural Search
|
||||
- **Functionality**: Semantic search technology for finding related content
|
||||
- **Best for**: Discovering content based on conceptual similarity rather than keyword matching
|
||||
- **Key features**: Find similar articles, competitor analysis, and content inspiration
|
||||
|
||||
### 3. Trend Analysis Tools
|
||||
|
||||
#### Google Trends Researcher
|
||||
- **Functionality**: Analyzes search term popularity over time
|
||||
- **Best for**: Content planning, seasonal topic identification, and trend forecasting
|
||||
- **Key features**: Interest over time visualization, regional interest mapping, and related query analysis
|
||||
|
||||
### 4. Web Crawling & Analysis Tools
|
||||
|
||||
#### Async Web Crawler
|
||||
- **Functionality**: Crawls websites to extract structured content
|
||||
- **Best for**: Content auditing, competitor analysis, and data extraction
|
||||
- **Key features**: Extracts titles, descriptions, main content, headings, links, and images
|
||||
|
||||
#### Firecrawl Web Crawler
|
||||
- **Functionality**: Specialized crawler for single-page or website scraping
|
||||
- **Best for**: Detailed content extraction from specific URLs
|
||||
- **Key features**: Configurable depth and page limits for targeted crawling
|
||||
|
||||
#### Website Analyzer
|
||||
- **Functionality**: Comprehensive website analysis for content and technical aspects
|
||||
- **Best for**: Content quality assessment, SEO analysis, and technical audits
|
||||
- **Key features**: Content quality metrics, SEO recommendations, and technical performance insights
|
||||
|
||||
## Optimal Workflows for Content Creators
|
||||
|
||||
### Research Workflow 1: Comprehensive Topic Research
|
||||
|
||||
1. **Initial Exploration** (Google SERP Search)
|
||||
- Search for your main topic to understand the search landscape
|
||||
- Analyze People Also Ask questions to identify user intent
|
||||
|
||||
2. **In-depth Research** (Tavily AI Search)
|
||||
- Use identified subtopics for deeper research
|
||||
- Collect factual information and expert insights
|
||||
|
||||
3. **Competitive Analysis** (Metaphor Neural Search + Web Crawler)
|
||||
- Find similar content from competitors
|
||||
- Analyze content structure and approach
|
||||
|
||||
4. **Trend Validation** (Google Trends Researcher)
|
||||
- Verify topic popularity and seasonal patterns
|
||||
- Identify related trending topics
|
||||
|
||||
### Research Workflow 2: Content Gap Analysis
|
||||
|
||||
1. **Competitor Content Mapping** (Async Web Crawler)
|
||||
- Crawl competitor websites to extract content structure
|
||||
- Identify their content categories and topics
|
||||
|
||||
2. **Topic Opportunity Discovery** (Google Trends + Metaphor Search)
|
||||
- Research trending topics in your niche
|
||||
- Find content areas with high interest but low competition
|
||||
|
||||
3. **Content Quality Benchmarking** (Website Analyzer)
|
||||
- Analyze top-performing content in your niche
|
||||
- Identify quality benchmarks and content standards
|
||||
|
||||
### Research Workflow 3: Content Refresh & Update
|
||||
|
||||
1. **Content Performance Analysis** (Website Analyzer)
|
||||
- Analyze existing content for improvement opportunities
|
||||
- Identify outdated information and gaps
|
||||
|
||||
2. **Current Trend Integration** (Google Trends Researcher)
|
||||
- Research current trends related to your content
|
||||
- Identify new angles and perspectives
|
||||
|
||||
3. **Competitive Edge Research** (Tavily AI + Metaphor Search)
|
||||
- Research what competitors have updated recently
|
||||
- Find new research, statistics, and information to incorporate
|
||||
|
||||
## Integration with Content Creation Process
|
||||
|
||||
### Pre-Writing Phase
|
||||
- Use research modules to gather comprehensive information
|
||||
- Create content briefs based on research findings
|
||||
- Identify key points, statistics, and examples to include
|
||||
|
||||
### Writing Phase
|
||||
- Reference research findings for accurate information
|
||||
- Use competitor insights to differentiate your content
|
||||
- Incorporate trending topics and keywords
|
||||
|
||||
### Post-Publishing Phase
|
||||
- Monitor content performance against researched benchmarks
|
||||
- Update content based on new research findings
|
||||
- Plan related content based on research insights
|
||||
|
||||
## Dashboard Usage Examples
|
||||
|
||||
### Example 1: Creating a Comprehensive Blog Post
|
||||
|
||||
1. Start with Google SERP Search for your main topic
|
||||
2. Use Tavily AI Search to gather in-depth information on subtopics
|
||||
3. Analyze competitor content with Metaphor Neural Search
|
||||
4. Check topic seasonality with Google Trends Researcher
|
||||
5. Create content that addresses all aspects discovered in research
|
||||
|
||||
### Example 2: Developing a Content Strategy
|
||||
|
||||
1. Use Google Trends to identify trending topics in your niche
|
||||
2. Analyze competitor websites with Async Web Crawler
|
||||
3. Research content gaps using Metaphor Neural Search
|
||||
4. Prioritize content topics based on trend data and competition
|
||||
5. Create a content calendar incorporating research insights
|
||||
|
||||
### Example 3: Updating Existing Content
|
||||
|
||||
1. Analyze current content with Website Analyzer
|
||||
2. Research new developments with Tavily AI Search
|
||||
3. Check for changing trends with Google Trends Researcher
|
||||
4. Identify new angles with Metaphor Neural Search
|
||||
5. Update content with new information and perspectives
|
||||
|
||||
## Best Practices for Effective Research
|
||||
|
||||
1. **Start Broad, Then Narrow**: Begin with general search tools before using specialized ones
|
||||
2. **Combine Multiple Research Methods**: Use different modules for comprehensive insights
|
||||
3. **Focus on User Intent**: Prioritize research that reveals what your audience wants
|
||||
4. **Validate with Data**: Use trend analysis to validate topic importance
|
||||
5. **Organize Research Findings**: Create structured notes from your research
|
||||
6. **Set Research Goals**: Define what you need to learn before starting
|
||||
7. **Schedule Regular Research**: Keep content updated with periodic research
|
||||
|
||||
## Technical Requirements
|
||||
|
||||
- API keys for various services (Google, Tavily, Metaphor, etc.)
|
||||
- Proper configuration in the .env file
|
||||
- Sufficient system resources for web crawling operations
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- Integration with content planning calendar
|
||||
- Automated research reports
|
||||
- Competitive content gap analysis
|
||||
- AI-powered content brief generation
|
||||
- Customizable research workflows
|
||||
|
||||
---
|
||||
|
||||
This dashboard is designed to streamline the research process for content creators, providing powerful tools to enhance content quality, relevance, and performance. By following the suggested workflows and best practices, content creators can significantly improve their research efficiency and content outcomes.
|
||||
98
lib/alwrity_ui/alwrity_researcher/README_DASHBOARD.md
Normal file
98
lib/alwrity_ui/alwrity_researcher/README_DASHBOARD.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# AI Web Researcher Dashboard
|
||||
|
||||
## Overview
|
||||
|
||||
The AI Web Researcher Dashboard is a modern, intuitive interface designed specifically for content creators and digital marketing professionals. This dashboard integrates various web research tools to streamline the content research process, enhance content quality, and improve workflow efficiency.
|
||||
|
||||
## Features
|
||||
|
||||
### 1. Intuitive User Interface
|
||||
- Modern, colorful, and professional design
|
||||
- Easy navigation through different research tools
|
||||
- Responsive layout that works on various screen sizes
|
||||
|
||||
### 2. Integrated Research Tools
|
||||
- **Search Engine Research**: Google SERP Search and Tavily AI Search
|
||||
- **Neural Search**: Metaphor Neural Search for finding conceptually similar content
|
||||
- **Trend Analysis**: Google Trends Researcher for analyzing search term popularity
|
||||
- **Web Crawling & Analysis**: Tools for extracting and analyzing web content
|
||||
|
||||
### 3. Guided Research Workflows
|
||||
- Comprehensive Topic Research workflow
|
||||
- Content Gap Analysis workflow
|
||||
- Content Refresh & Update workflow
|
||||
|
||||
## Installation
|
||||
|
||||
1. Ensure you have Python 3.8+ installed
|
||||
2. Install the required dependencies:
|
||||
```
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
3. Set up the necessary API keys in your environment variables or .env file
|
||||
|
||||
## Usage
|
||||
|
||||
1. Navigate to the dashboard directory:
|
||||
```
|
||||
cd AI-Writer/lib/alwrity_ui/alwrity_researcher
|
||||
```
|
||||
|
||||
2. Run the dashboard:
|
||||
```
|
||||
python -m streamlit run main.py
|
||||
```
|
||||
|
||||
3. Access the dashboard in your web browser at http://localhost:8501
|
||||
|
||||
## Dashboard Sections
|
||||
|
||||
### Dashboard Home
|
||||
Provides an overview of available research tools and featured workflows.
|
||||
|
||||
### Search Tools
|
||||
Access to Google SERP Search and Tavily AI Search for comprehensive search capabilities.
|
||||
|
||||
### Neural Search
|
||||
Use Metaphor Neural Search to find content based on conceptual similarity rather than keyword matching.
|
||||
|
||||
### Trend Analysis
|
||||
Analyze search term popularity over time using Google Trends Researcher.
|
||||
|
||||
### Web Crawling
|
||||
Extract structured content from websites using various web crawling tools.
|
||||
|
||||
### Research Workflows
|
||||
Guided workflows that combine multiple tools for specific research objectives.
|
||||
|
||||
## Current Implementation Status
|
||||
|
||||
This is the initial implementation of the dashboard with placeholder functionality. The UI is fully implemented, but the actual integration with the AI web research modules will be completed in the next phase.
|
||||
|
||||
### What's Implemented
|
||||
- Complete user interface with all sections and forms
|
||||
- Mock data visualization for demonstration purposes
|
||||
- Placeholder functionality for all research tools
|
||||
|
||||
### Next Steps
|
||||
- Integrate with actual AI web research modules
|
||||
- Implement data processing and visualization with real data
|
||||
- Add user authentication and result saving functionality
|
||||
|
||||
## Technical Details
|
||||
|
||||
- Built with Streamlit for rapid UI development
|
||||
- Modular design for easy extension and maintenance
|
||||
- Responsive layout that works on desktop and mobile devices
|
||||
|
||||
## File Structure
|
||||
|
||||
- `main.py`: Entry point for the application
|
||||
- `dashboard.py`: Main dashboard implementation
|
||||
- `utils.py`: Utility functions for data processing and visualization
|
||||
- `style.css`: Custom CSS for styling the dashboard
|
||||
- `requirements.txt`: Required Python packages
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
66
lib/alwrity_ui/alwrity_researcher/config.py
Normal file
66
lib/alwrity_ui/alwrity_researcher/config.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import os
|
||||
import streamlit as st
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
def get_api_key(key_name):
|
||||
"""Get API key from environment variables or Streamlit secrets"""
|
||||
# Try to get from environment variables first
|
||||
api_key = os.getenv(key_name)
|
||||
|
||||
# If not found in environment, try Streamlit secrets
|
||||
if not api_key and key_name in st.secrets:
|
||||
api_key = st.secrets[key_name]
|
||||
|
||||
return api_key
|
||||
|
||||
def check_api_configuration():
|
||||
"""Check if all required API keys are configured"""
|
||||
api_status = {
|
||||
"SERPER_API_KEY": bool(get_api_key("SERPER_API_KEY")),
|
||||
"TAVILY_API_KEY": bool(get_api_key("TAVILY_API_KEY")),
|
||||
"METAPHOR_API_KEY": bool(get_api_key("METAPHOR_API_KEY")),
|
||||
"FIRECRAWL_API_KEY": bool(get_api_key("FIRECRAWL_API_KEY"))
|
||||
}
|
||||
|
||||
return api_status
|
||||
|
||||
def display_api_configuration_status():
|
||||
"""Display API configuration status in the sidebar with improved styling"""
|
||||
api_status = check_api_configuration()
|
||||
|
||||
st.sidebar.markdown("<div class='api-status-container'>", unsafe_allow_html=True)
|
||||
st.sidebar.markdown("<div class='api-status-title'>API Configuration Status</div>", unsafe_allow_html=True)
|
||||
|
||||
# Display API status with improved styling
|
||||
for api_name, is_configured in api_status.items():
|
||||
if is_configured:
|
||||
st.sidebar.markdown(
|
||||
f"<div class='api-status-item configured'>✅ {api_name}</div>",
|
||||
unsafe_allow_html=True
|
||||
)
|
||||
else:
|
||||
st.sidebar.markdown(
|
||||
f"<div class='api-status-item not-configured'>❌ {api_name}</div>",
|
||||
unsafe_allow_html=True
|
||||
)
|
||||
|
||||
# Display instructions if any API key is missing with improved styling
|
||||
if not all(api_status.values()):
|
||||
with st.sidebar.expander("How to configure API keys"):
|
||||
st.markdown("""
|
||||
<div style="background-color: #f8fafc; padding: 12px; border-radius: 6px; border-left: 4px solid #3b82f6;">
|
||||
<p style="margin-bottom: 10px; font-weight: 500;">To configure missing API keys, create a <code>.env</code> file in the project root with the following format:</p>
|
||||
<pre style="background-color: #f1f5f9; padding: 10px; border-radius: 4px; overflow-x: auto; font-size: 0.9em;">
|
||||
SERPER_API_KEY=your_serper_api_key
|
||||
TAVILY_API_KEY=your_tavily_api_key
|
||||
METAPHOR_API_KEY=your_metaphor_api_key
|
||||
FIRECRAWL_API_KEY=your_firecrawl_api_key
|
||||
</pre>
|
||||
<p style="margin-top: 10px;">Alternatively, you can set these as environment variables in your system.</p>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
st.sidebar.markdown("</div>", unsafe_allow_html=True)
|
||||
729
lib/alwrity_ui/alwrity_researcher/dashboard.py
Normal file
729
lib/alwrity_ui/alwrity_researcher/dashboard.py
Normal file
@@ -0,0 +1,729 @@
|
||||
import streamlit as st
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Add parent directory to path to import modules
|
||||
sys.path.append(str(Path(__file__).parent.parent.parent))
|
||||
|
||||
# Import utils module
|
||||
from utils import (
|
||||
load_css,
|
||||
display_google_serp_results,
|
||||
display_tavily_results,
|
||||
display_metaphor_results,
|
||||
display_google_trends_results,
|
||||
display_crawler_results,
|
||||
display_analyzer_results
|
||||
)
|
||||
|
||||
# Configure Streamlit page settings
|
||||
st.set_page_config(
|
||||
page_title="AI Web Researcher Dashboard",
|
||||
page_icon="🔍",
|
||||
layout="wide",
|
||||
initial_sidebar_state="expanded"
|
||||
)
|
||||
|
||||
# Apply custom CSS immediately
|
||||
st.markdown("""
|
||||
<style>
|
||||
.main-header {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.category-header {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin: 15px 0;
|
||||
color: #0066cc;
|
||||
}
|
||||
/* Make the dashboard responsive */
|
||||
.stTabs [data-baseweb="tab-list"] {
|
||||
gap: 2px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.stTabs [data-baseweb="tab"] {
|
||||
white-space: pre-wrap;
|
||||
min-width: fit-content;
|
||||
font-size: 14px;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
/* Adjust container width */
|
||||
.block-container {
|
||||
max-width: 95% !important;
|
||||
padding: 1rem 1rem 10rem !important;
|
||||
}
|
||||
/* Additional styling for better visibility */
|
||||
.stApp {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.stTabs [data-baseweb="tab"] [data-testid="stMarkdownContainer"] p {
|
||||
font-size: 14px !important;
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
.stTabs [data-baseweb="tab-list"] button {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 4px;
|
||||
margin: 2px;
|
||||
}
|
||||
.stTabs [data-baseweb="tab-list"] button[aria-selected="true"] {
|
||||
background-color: #e7f1ff;
|
||||
border-color: #b8daff;
|
||||
}
|
||||
</style>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# CSS is now loaded at the top of the file
|
||||
|
||||
# Initialize session state variables
|
||||
def init_session_state():
|
||||
if 'current_section' not in st.session_state:
|
||||
st.session_state['current_section'] = 'Dashboard Home'
|
||||
|
||||
# Initialize session state at startup
|
||||
init_session_state()
|
||||
|
||||
def main():
|
||||
# Initialize session state before accessing it
|
||||
init_session_state()
|
||||
|
||||
# Main navigation header
|
||||
st.markdown('<div class="main-nav-header">AI Web Researcher</div>', unsafe_allow_html=True)
|
||||
|
||||
# Create tabs for navigation
|
||||
selected_section = st.tabs([
|
||||
"🏠 Dashboard Home",
|
||||
"🔍 Search Tools",
|
||||
"🧠 Neural Search",
|
||||
"📈 Trend Analysis",
|
||||
"🕸️ Web Crawling",
|
||||
"📚 Academic Research",
|
||||
"📋 Research Workflows"
|
||||
])
|
||||
|
||||
# Display appropriate section based on selected tab
|
||||
with selected_section[0]:
|
||||
display_home()
|
||||
with selected_section[1]:
|
||||
display_search_tools()
|
||||
with selected_section[2]:
|
||||
display_neural_search()
|
||||
with selected_section[3]:
|
||||
display_trend_analysis()
|
||||
with selected_section[4]:
|
||||
display_web_crawling()
|
||||
with selected_section[5]:
|
||||
display_academic_research()
|
||||
with selected_section[6]:
|
||||
display_research_workflows()
|
||||
|
||||
# Ensure CSS is consistently applied
|
||||
load_css()
|
||||
|
||||
def display_home():
|
||||
|
||||
# Main header with improved styling
|
||||
st.markdown('<div class="main-header">AI Web Researcher Dashboard</div>', unsafe_allow_html=True)
|
||||
|
||||
# Introduction with better formatting
|
||||
st.markdown("""
|
||||
<div class="intro-container">
|
||||
<p class="intro-text">Welcome to the <span class="intro-highlight">AI Web Researcher Dashboard</span>, a comprehensive suite of research tools designed
|
||||
specifically for content creators and digital marketing professionals. This dashboard integrates
|
||||
various web research modules to streamline your content research process, enhance content quality,
|
||||
and improve workflow efficiency.</p>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Quick access to tool categories with improved header
|
||||
st.markdown('<div class="category-header">Research Tool Categories</div>', unsafe_allow_html=True)
|
||||
|
||||
# Use 2 columns with equal width for better layout
|
||||
col1, col2 = st.columns([1, 1])
|
||||
|
||||
with col1:
|
||||
st.markdown("""
|
||||
<div class="tool-card">
|
||||
<div class="tool-title">🔍 Search Engine Research</div>
|
||||
<div class="tool-description">Powerful search tools to understand search intent, identify content gaps, and discover high-performing content in your niche</div>
|
||||
<div class="tool-features"><b>Includes:</b> Google SERP Search • Tavily AI Search</div>
|
||||
<div class="tool-features"><b>Use cases:</b> Keyword research, competitor analysis, content planning, identifying user questions</div>
|
||||
<div style="margin-top: 12px;">
|
||||
<span class="tool-badge">SERP Analysis</span>
|
||||
<span class="tool-badge ai-powered">AI-Powered</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tool-card">
|
||||
<div class="tool-title">🧠 Neural Search</div>
|
||||
<div class="tool-description">Advanced semantic search technology that understands concepts rather than just keywords to find truly relevant content</div>
|
||||
<div class="tool-features"><b>Includes:</b> Metaphor Neural Search</div>
|
||||
<div class="tool-features"><b>Use cases:</b> Finding conceptually similar articles, discovering unique content angles, competitive research</div>
|
||||
<div style="margin-top: 12px;">
|
||||
<span class="tool-badge semantic">Semantic</span>
|
||||
<span class="tool-badge deep-learning">Deep Learning</span>
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
with col2:
|
||||
st.markdown("""
|
||||
<div class="tool-card">
|
||||
<div class="tool-title">📈 Trend Analysis</div>
|
||||
<div class="tool-description">Comprehensive trend analysis tools that track search term popularity over time to identify seasonal patterns and emerging topics</div>
|
||||
<div class="tool-features"><b>Includes:</b> Google Trends Researcher</div>
|
||||
<div class="tool-features"><b>Use cases:</b> Seasonal content planning, topic validation, identifying rising trends, content calendar optimization</div>
|
||||
<div style="margin-top: 12px;">
|
||||
<span class="tool-badge time-series">Time Series</span>
|
||||
<span class="tool-badge forecasting">Forecasting</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tool-card">
|
||||
<div class="tool-title">🕸️ Web Crawling & Analysis</div>
|
||||
<div class="tool-description">Powerful web extraction tools that gather and structure content from websites for in-depth analysis and insights</div>
|
||||
<div class="tool-features"><b>Includes:</b> Async Web Crawler • Firecrawl Web Crawler • Website Analyzer</div>
|
||||
<div class="tool-features"><b>Use cases:</b> Content auditing, competitor analysis, data extraction, market research, content aggregation</div>
|
||||
<div style="margin-top: 12px;">
|
||||
<span class="tool-badge content-extraction">Content Extraction</span>
|
||||
<span class="tool-badge data-analysis">Data Analysis</span>
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Featured workflow with improved header
|
||||
st.markdown('<div class="category-header">Featured Research Workflow</div>', unsafe_allow_html=True)
|
||||
|
||||
st.markdown("""
|
||||
<div class="workflow-card">
|
||||
<div class="workflow-title">Comprehensive Topic Research Workflow</div>
|
||||
<p style="color: #4b5563; margin-bottom: 1.25rem;">Follow this proven research workflow to develop comprehensive, data-driven content that resonates with your audience and performs well in search.</p>
|
||||
<div class="workflow-step-container">
|
||||
<div class="workflow-step-number">1</div>
|
||||
<div class="workflow-step-content">
|
||||
<div class="step-title">Initial Exploration</div>
|
||||
<div class="workflow-step-description">Use Google SERP Search to understand the search landscape, identify user intent, and discover what content currently ranks well. Analyze People Also Ask questions to identify key user concerns.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workflow-step-container">
|
||||
<div class="workflow-step-number">2</div>
|
||||
<div class="workflow-step-content">
|
||||
<div class="step-title">In-depth Research</div>
|
||||
<div class="workflow-step-description">Use Tavily AI Search for deeper research on identified subtopics. The AI-powered search provides more contextual information and helps uncover expert insights that might be missed in traditional searches.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workflow-step-container">
|
||||
<div class="workflow-step-number">3</div>
|
||||
<div class="workflow-step-content">
|
||||
<div class="step-title">Competitive Analysis</div>
|
||||
<div class="workflow-step-description">Use Metaphor Neural Search to find conceptually similar content from competitors. Analyze their approach, identify content gaps, and discover unique angles that differentiate your content.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workflow-step-container">
|
||||
<div class="workflow-step-number">4</div>
|
||||
<div class="workflow-step-content">
|
||||
<div class="step-title">Trend Validation</div>
|
||||
<div class="workflow-step-description">Use Google Trends Researcher to verify topic popularity, identify seasonal patterns, and discover related trending topics. This ensures your content is timely and aligned with current audience interests.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workflow-step-container">
|
||||
<div class="workflow-step-number">5</div>
|
||||
<div class="workflow-step-content">
|
||||
<div class="step-title">Content Extraction</div>
|
||||
<div class="workflow-step-description">Use Web Crawling tools to extract specific content from top-performing pages for detailed analysis. This helps identify content structure, depth, and formatting approaches that resonate with your audience.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
def display_search_tools():
|
||||
|
||||
st.markdown('<div class="main-header">Search Engine Research Tools</div>', unsafe_allow_html=True)
|
||||
|
||||
# Google SERP Search
|
||||
st.markdown('<div class="category-header">Google SERP Search</div>', unsafe_allow_html=True)
|
||||
|
||||
st.markdown("""
|
||||
<div class="tool-card">
|
||||
<div class="tool-title">Google SERP Search</div>
|
||||
<div class="tool-description">Retrieves organic search results, People Also Ask questions, and related searches</div>
|
||||
<div class="tool-features">
|
||||
<b>Best for:</b> Initial topic research, understanding search intent, and identifying content gaps<br>
|
||||
<b>Key features:</b> Comprehensive search results with structured data extraction
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Input form for Google SERP Search
|
||||
with st.form("google_serp_search_form"):
|
||||
search_query = st.text_input("Enter your search query")
|
||||
col1, col2 = st.columns(2)
|
||||
with col1:
|
||||
num_results = st.slider("Number of results", 5, 30, 10)
|
||||
with col2:
|
||||
include_paa = st.checkbox("Include People Also Ask", value=True)
|
||||
|
||||
submitted = st.form_submit_button("Search")
|
||||
if submitted and search_query:
|
||||
try:
|
||||
with st.spinner("Searching Google SERP..."):
|
||||
# Import the actual module
|
||||
from ai_web_researcher.google_serp_search import google_search
|
||||
|
||||
# Call the actual implementation
|
||||
results = google_search(search_query)
|
||||
|
||||
# Display the results
|
||||
if results:
|
||||
display_google_serp_results(results)
|
||||
else:
|
||||
st.error("No results found. Please try a different query.")
|
||||
except Exception as e:
|
||||
st.error(f"Error performing Google SERP search: {str(e)}")
|
||||
st.info("Please check your API configuration in the .env file.")
|
||||
st.info("Required API: SERPER_API_KEY for Google SERP Search")
|
||||
display_google_serp_results(None)
|
||||
|
||||
# Tavily AI Search
|
||||
st.markdown('<div class="category-header">Tavily AI Search</div>', unsafe_allow_html=True)
|
||||
|
||||
st.markdown("""
|
||||
<div class="tool-card">
|
||||
<div class="tool-title">Tavily AI Search</div>
|
||||
<div class="tool-description">Advanced AI-powered search with semantic understanding</div>
|
||||
<div class="tool-features">
|
||||
<b>Best for:</b> In-depth research requiring contextual understanding<br>
|
||||
<b>Key features:</b> Provides direct answers to questions and follow-up question suggestions
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Input form for Tavily AI Search
|
||||
with st.form("tavily_ai_search_form"):
|
||||
search_query = st.text_input("Enter your search query or question")
|
||||
search_depth = st.select_slider("Search depth", options=["basic", "medium", "deep"], value="medium")
|
||||
|
||||
submitted = st.form_submit_button("Search with Tavily AI")
|
||||
if submitted and search_query:
|
||||
try:
|
||||
with st.spinner("Searching with Tavily AI..."):
|
||||
# Import the actual module
|
||||
from ai_web_researcher.tavily_ai_search import do_tavily_ai_search
|
||||
|
||||
# Call the actual implementation
|
||||
results = do_tavily_ai_search(search_query, search_depth=search_depth)
|
||||
|
||||
# Display the results
|
||||
if results:
|
||||
display_tavily_results(results)
|
||||
else:
|
||||
st.error("No results found. Please try a different query.")
|
||||
except Exception as e:
|
||||
st.error(f"Error performing Tavily AI search: {str(e)}")
|
||||
st.info("Please check your API configuration in the .env file.")
|
||||
st.info("Required API: TAVILY_API_KEY for Tavily AI Search")
|
||||
display_tavily_results(None)
|
||||
|
||||
def display_neural_search():
|
||||
|
||||
st.markdown('<div class="main-header">Neural Search Tools</div>', unsafe_allow_html=True)
|
||||
|
||||
# Metaphor Neural Search
|
||||
st.markdown('<div class="category-header">Metaphor Neural Search</div>', unsafe_allow_html=True)
|
||||
|
||||
st.markdown("""
|
||||
<div class="tool-card">
|
||||
<div class="tool-title">Metaphor Neural Search</div>
|
||||
<div class="tool-description">Semantic search technology for finding related content</div>
|
||||
<div class="tool-features">
|
||||
<b>Best for:</b> Discovering content based on conceptual similarity rather than keyword matching<br>
|
||||
<b>Key features:</b> Find similar articles, competitor analysis, and content inspiration
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Input form for Metaphor Neural Search
|
||||
with st.form("metaphor_search_form"):
|
||||
st.markdown("**Search by keyword or find similar content to a URL**")
|
||||
search_type = st.radio("Search type", ["Keyword Search", "Similar Content Search"])
|
||||
|
||||
if search_type == "Keyword Search":
|
||||
search_query = st.text_input("Enter your search query")
|
||||
url_input = ""
|
||||
else:
|
||||
search_query = ""
|
||||
url_input = st.text_input("Enter a URL to find similar content")
|
||||
|
||||
num_results = st.slider("Number of results", 3, 20, 5)
|
||||
|
||||
submitted = st.form_submit_button("Search with Metaphor")
|
||||
if submitted and (search_query or url_input):
|
||||
try:
|
||||
with st.spinner("Searching with Metaphor Neural Search..."):
|
||||
# Import the actual module
|
||||
from ai_web_researcher.metaphor_basic_neural_web_search import metaphor_search_articles, metaphor_find_similar
|
||||
|
||||
# Call the actual implementation
|
||||
if search_query:
|
||||
results = metaphor_search_articles(search_query, num_results=num_results)
|
||||
else:
|
||||
results = metaphor_find_similar(url_input, num_results=num_results)
|
||||
|
||||
# Display the results
|
||||
if results:
|
||||
display_metaphor_results(results)
|
||||
else:
|
||||
st.error("No results found. Please try a different query.")
|
||||
except Exception as e:
|
||||
st.error(f"Error performing Metaphor Neural search: {str(e)}")
|
||||
st.info("Please check your API configuration in the .env file.")
|
||||
st.info("Required API: METAPHOR_API_KEY for Metaphor Neural Search")
|
||||
display_metaphor_results(None)
|
||||
|
||||
def display_trend_analysis():
|
||||
|
||||
st.markdown('<div class="main-header">Trend Analysis Tools</div>', unsafe_allow_html=True)
|
||||
|
||||
# Google Trends Researcher
|
||||
st.markdown('<div class="category-header">Google Trends Researcher</div>', unsafe_allow_html=True)
|
||||
|
||||
st.markdown("""
|
||||
<div class="tool-card">
|
||||
<div class="tool-title">Google Trends Researcher</div>
|
||||
<div class="tool-description">Analyze search term popularity and related queries</div>
|
||||
<div class="tool-features">
|
||||
<b>Best for:</b> Content planning, trend forecasting, and seasonal content optimization<br>
|
||||
<b>Key features:</b> Interest over time charts, related queries, and regional interest data
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Input form for Google Trends Researcher
|
||||
with st.form("google_trends_form"):
|
||||
search_terms = st.text_area("Enter search terms (one per line)")
|
||||
col1, col2 = st.columns(2)
|
||||
with col1:
|
||||
time_frame = st.select_slider("Time range", options=["past_hour", "past_day", "past_week", "past_month", "past_90_days", "past_12_months", "past_5_years"], value="past_12_months")
|
||||
with col2:
|
||||
geo = st.text_input("Geographic region (ISO country code, e.g., 'US')", value="US")
|
||||
|
||||
submitted = st.form_submit_button("Analyze Trends")
|
||||
if submitted and search_terms:
|
||||
try:
|
||||
with st.spinner("Analyzing Google Trends..."):
|
||||
# Import the actual module
|
||||
from ai_web_researcher.google_trends_researcher import do_google_trends_analysis
|
||||
|
||||
# Call the actual implementation
|
||||
search_terms_list = [term.strip() for term in search_terms.split('\n') if term.strip()]
|
||||
results = do_google_trends_analysis(search_terms_list, time_frame=time_frame, geo=geo)
|
||||
|
||||
# Display the results
|
||||
if results:
|
||||
display_google_trends_results(results)
|
||||
else:
|
||||
st.error("No trend data found. Please try different search terms.")
|
||||
except Exception as e:
|
||||
st.error(f"Error analyzing Google Trends: {str(e)}")
|
||||
st.info("Google Trends analysis doesn't require an API key, but there might be rate limiting or network issues.")
|
||||
display_google_trends_results(None)
|
||||
|
||||
def display_web_crawling():
|
||||
|
||||
st.markdown('<div class="main-header">Web Crawling & Analysis Tools</div>', unsafe_allow_html=True)
|
||||
|
||||
# Create tabs for different crawling tools
|
||||
tab1, tab2, tab3 = st.tabs(["Firecrawl Web Crawler", "Website Analyzer", "Async Web Crawler"])
|
||||
|
||||
with tab1:
|
||||
st.markdown("""
|
||||
<div class="tool-card">
|
||||
<div class="tool-title">Firecrawl Web Crawler</div>
|
||||
<div class="tool-description">Extract structured content from websites</div>
|
||||
<div class="tool-features">
|
||||
<b>Best for:</b> Content extraction, competitor analysis, and website auditing<br>
|
||||
<b>Key features:</b> Extracts titles, descriptions, headings, and content from web pages
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Input form for Firecrawl Web Crawler
|
||||
with st.form("firecrawl_form"):
|
||||
website_url = st.text_input("Enter website URL")
|
||||
depth = st.slider("Crawl depth", 1, 5, 1)
|
||||
max_pages = st.slider("Maximum pages", 1, 50, 10)
|
||||
|
||||
submitted = st.form_submit_button("Crawl Website")
|
||||
if submitted and website_url:
|
||||
try:
|
||||
with st.spinner("Crawling website..."):
|
||||
# Import the actual module
|
||||
from ai_web_researcher.firecrawl_web_crawler import scrape_website
|
||||
|
||||
# Call the actual implementation
|
||||
results = scrape_website(website_url, depth=depth, max_pages=max_pages)
|
||||
|
||||
# Display the results
|
||||
if results:
|
||||
display_crawler_results(results)
|
||||
else:
|
||||
st.error("No crawler results found. Please try a different URL.")
|
||||
except Exception as e:
|
||||
st.error(f"Error crawling website: {str(e)}")
|
||||
st.info("Please check your API configuration in the .env file.")
|
||||
st.info("Required API: FIRECRAWL_API_KEY for Web Crawler")
|
||||
display_crawler_results(None)
|
||||
|
||||
with tab2:
|
||||
st.markdown("""
|
||||
<div class="tool-card">
|
||||
<div class="tool-title">Website Analyzer</div>
|
||||
<div class="tool-description">Analyze website content and structure</div>
|
||||
<div class="tool-features">
|
||||
<b>Best for:</b> Content analysis, SEO auditing, and competitor research<br>
|
||||
<b>Key features:</b> Content analysis, keyword extraction, and readability metrics
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Input form for Website Analyzer
|
||||
with st.form("website_analyzer_form"):
|
||||
website_url = st.text_input("Enter website URL")
|
||||
analyze_type = st.selectbox("Analysis type", ["Basic Analysis", "SEO Analysis", "Content Analysis", "Competitor Analysis"])
|
||||
|
||||
submitted = st.form_submit_button("Analyze Website")
|
||||
if submitted and website_url:
|
||||
st.info("Website Analyzer is coming soon. This feature is under development.")
|
||||
|
||||
with tab3:
|
||||
st.markdown("""
|
||||
<div class="tool-card">
|
||||
<div class="tool-title">Async Web Crawler</div>
|
||||
<div class="tool-description">High-performance asynchronous web crawler</div>
|
||||
<div class="tool-features">
|
||||
<b>Best for:</b> Large-scale crawling, data extraction, and content aggregation<br>
|
||||
<b>Key features:</b> Fast, efficient crawling with customizable extraction rules
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Input form for Async Web Crawler
|
||||
with st.form("async_crawler_form"):
|
||||
website_url = st.text_input("Enter website URL")
|
||||
max_urls = st.slider("Maximum URLs to crawl", 10, 100, 30)
|
||||
|
||||
submitted = st.form_submit_button("Start Crawling")
|
||||
if submitted and website_url:
|
||||
st.info("Async Web Crawler is coming soon. This feature is under development.")
|
||||
|
||||
def display_academic_research():
|
||||
|
||||
st.markdown('<div class="main-header">Academic Research Tools</div>', unsafe_allow_html=True)
|
||||
|
||||
# ArXiv Search Section
|
||||
st.markdown('<div class="category-header">ArXiv Scholarly Search</div>', unsafe_allow_html=True)
|
||||
|
||||
# Search Parameters
|
||||
search_col1, search_col2 = st.columns([2, 1])
|
||||
with search_col1:
|
||||
search_query = st.text_input("🔍 Enter research topic or keywords", key="arxiv_search")
|
||||
with search_col2:
|
||||
max_results = st.number_input("Maximum Results", min_value=1, max_value=50, value=10)
|
||||
|
||||
# Search Button
|
||||
if st.button("🔎 Search ArXiv", key="arxiv_search_button"):
|
||||
if search_query:
|
||||
with st.spinner("Searching ArXiv database..."):
|
||||
try:
|
||||
# Import arxiv search function
|
||||
from ai_web_researcher.arxiv_schlorly_research import fetch_arxiv_data, create_dataframe
|
||||
|
||||
# Fetch results
|
||||
results = fetch_arxiv_data(search_query, max_results)
|
||||
|
||||
if results:
|
||||
# Create DataFrame
|
||||
df = create_dataframe(results, ["Title", "Published Date", "ArXiv ID", "Summary", "PDF URL"])
|
||||
|
||||
# Display results in an expander
|
||||
with st.expander("📚 Search Results", expanded=True):
|
||||
# Display each paper with options to view abstract and download
|
||||
for idx, row in df.iterrows():
|
||||
st.markdown(f"### {row['Title']}")
|
||||
st.markdown(f"*Published: {row['Published Date']}*")
|
||||
|
||||
# Create columns for buttons
|
||||
btn_col1, btn_col2, btn_col3 = st.columns([1, 1, 1])
|
||||
|
||||
with btn_col1:
|
||||
if st.button(f"📄 View Abstract #{idx}"):
|
||||
st.markdown(f"**Abstract:**\n{row['Summary']}")
|
||||
|
||||
with btn_col2:
|
||||
st.markdown(f"[📥 Download PDF]({row['PDF URL']})")
|
||||
if st.button(f"📝 Summarize #{idx}"):
|
||||
with st.spinner("Generating summary..."):
|
||||
try:
|
||||
from ai_web_researcher.gpt_summarize_web_content import summarize_web_content
|
||||
summary = summarize_web_content(row['PDF URL'])
|
||||
if summary:
|
||||
st.markdown("### GPT Summary")
|
||||
st.markdown(summary)
|
||||
# Add export option for the summary
|
||||
st.download_button(
|
||||
label="📥 Export Summary",
|
||||
data=summary,
|
||||
file_name=f"summary_{row['ArXiv ID']}.txt",
|
||||
mime="text/plain"
|
||||
)
|
||||
except Exception as e:
|
||||
st.error(f"Error generating summary: {str(e)}")
|
||||
|
||||
with btn_col3:
|
||||
if st.button(f"🔍 Related Web Content #{idx}"):
|
||||
# Use Google SERP to find related content
|
||||
from ai_web_researcher.google_serp_search import google_search
|
||||
web_results = google_search(row['Title'])
|
||||
if web_results:
|
||||
st.markdown("### Related Web Content")
|
||||
for result in web_results['organic'][:3]:
|
||||
st.markdown(f"- [{result['title']}]({result['link']})\n {result['snippet']}")
|
||||
|
||||
st.markdown("---")
|
||||
else:
|
||||
st.warning("No results found. Try modifying your search terms.")
|
||||
except Exception as e:
|
||||
st.error(f"An error occurred while searching: {str(e)}")
|
||||
else:
|
||||
st.warning("Please enter a search query.")
|
||||
|
||||
# Research Notes Section
|
||||
st.markdown('<div class="category-header">Research Notes</div>', unsafe_allow_html=True)
|
||||
|
||||
# Initialize session state for notes if not exists
|
||||
if 'research_notes' not in st.session_state:
|
||||
st.session_state.research_notes = {}
|
||||
|
||||
notes_col1, notes_col2 = st.columns([2, 1])
|
||||
|
||||
with notes_col1:
|
||||
paper_id = st.text_input("ArXiv ID or Paper Title", key="notes_paper_id")
|
||||
notes_content = st.text_area("Research Notes", height=200, key="notes_content")
|
||||
|
||||
if st.button("Save Notes"):
|
||||
if paper_id:
|
||||
st.session_state.research_notes[paper_id] = notes_content
|
||||
st.success("Notes saved successfully!")
|
||||
else:
|
||||
st.warning("Please enter a paper identifier.")
|
||||
|
||||
with notes_col2:
|
||||
st.markdown("### Saved Notes")
|
||||
for paper_id, notes in st.session_state.research_notes.items():
|
||||
with st.expander(f"📝 {paper_id}"):
|
||||
st.text_area("Saved Notes", value=notes, height=150, key=f"saved_{paper_id}", disabled=True)
|
||||
if st.button("Export Notes", key=f"export_{paper_id}"):
|
||||
notes_export = f"Research Notes for {paper_id}\n\n{notes}"
|
||||
st.download_button(
|
||||
label="📥 Download Notes",
|
||||
data=notes_export,
|
||||
file_name=f"research_notes_{paper_id}.txt",
|
||||
mime="text/plain"
|
||||
)
|
||||
|
||||
# Citation Management Section
|
||||
st.markdown('<div class="category-header">Citation Management</div>', unsafe_allow_html=True)
|
||||
|
||||
# BibTeX Export
|
||||
st.markdown("### Export Citations")
|
||||
arxiv_id = st.text_input("Enter ArXiv ID for citation export")
|
||||
if arxiv_id and st.button("Generate BibTeX"):
|
||||
try:
|
||||
from ai_web_researcher.arxiv_schlorly_research import arxiv_bibtex
|
||||
bibtex = arxiv_bibtex(arxiv_id)
|
||||
if bibtex:
|
||||
st.code(bibtex, language="bibtex")
|
||||
st.download_button(
|
||||
label="📥 Download BibTeX",
|
||||
data=bibtex,
|
||||
file_name=f"citation_{arxiv_id}.bib",
|
||||
mime="text/plain"
|
||||
)
|
||||
except Exception as e:
|
||||
st.error(f"Error generating citation: {str(e)}")
|
||||
|
||||
def display_research_workflows():
|
||||
|
||||
st.markdown('<div class="main-header">Research Workflows</div>', unsafe_allow_html=True)
|
||||
|
||||
st.markdown("""
|
||||
<div class="workflow-description">
|
||||
Research workflows combine multiple research tools to provide comprehensive insights for specific content creation tasks.
|
||||
Select a workflow to get started.
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Create tabs for different workflows
|
||||
tab1, tab2, tab3 = st.tabs(["Topic Research", "Competitor Analysis", "Trend Discovery"])
|
||||
|
||||
with tab1:
|
||||
st.markdown("""
|
||||
<div class="workflow-card">
|
||||
<div class="workflow-title">Comprehensive Topic Research</div>
|
||||
<div class="workflow-description">
|
||||
This workflow helps you thoroughly research a topic for content creation by combining search results,
|
||||
semantic understanding, and trend analysis.
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Input form for Topic Research workflow
|
||||
with st.form("topic_research_form"):
|
||||
topic = st.text_input("Enter your topic")
|
||||
include_trends = st.checkbox("Include trend analysis", value=True)
|
||||
include_competitors = st.checkbox("Include competitor analysis", value=True)
|
||||
|
||||
submitted = st.form_submit_button("Start Research Workflow")
|
||||
if submitted and topic:
|
||||
st.info("Research workflows are coming soon. This feature is under development.")
|
||||
|
||||
with tab2:
|
||||
st.markdown("""
|
||||
<div class="workflow-card">
|
||||
<div class="workflow-title">Competitor Content Analysis</div>
|
||||
<div class="workflow-description">
|
||||
This workflow analyzes your competitors' content to identify gaps and opportunities for your own content strategy.
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Input form for Competitor Analysis workflow
|
||||
with st.form("competitor_analysis_form"):
|
||||
competitor_urls = st.text_area("Enter competitor URLs (one per line)")
|
||||
topic_focus = st.text_input("Topic focus (optional)")
|
||||
|
||||
submitted = st.form_submit_button("Start Competitor Analysis")
|
||||
if submitted and competitor_urls:
|
||||
st.info("Research workflows are coming soon. This feature is under development.")
|
||||
|
||||
with tab3:
|
||||
st.markdown("""
|
||||
<div class="workflow-card">
|
||||
<div class="workflow-title">Trend Discovery & Content Planning</div>
|
||||
<div class="workflow-description">
|
||||
This workflow identifies trending topics in your niche and helps you plan content around them.
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Input form for Trend Discovery workflow
|
||||
with st.form("trend_discovery_form"):
|
||||
niche = st.text_input("Enter your niche or industry")
|
||||
time_period = st.select_slider("Time period", options=["past_week", "past_month", "past_90_days", "past_12_months"], value="past_month")
|
||||
|
||||
submitted = st.form_submit_button("Discover Trends")
|
||||
if submitted and niche:
|
||||
st.info("Research workflows are coming soon. This feature is under development.")
|
||||
14
lib/alwrity_ui/alwrity_researcher/main.py
Normal file
14
lib/alwrity_ui/alwrity_researcher/main.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import streamlit as st
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Add the current directory to the path
|
||||
sys.path.append(str(Path(__file__).parent))
|
||||
|
||||
# Import the dashboard module
|
||||
from dashboard import main
|
||||
|
||||
# Run the dashboard
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
517
lib/alwrity_ui/alwrity_researcher/style.css
Normal file
517
lib/alwrity_ui/alwrity_researcher/style.css
Normal file
@@ -0,0 +1,517 @@
|
||||
/* Main Dashboard Styles */
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background-color: #f0f4f8;
|
||||
color: #1f2937;
|
||||
background-image: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
}
|
||||
|
||||
.main-header {
|
||||
font-size: 2.2rem;
|
||||
background: linear-gradient(120deg, #1e3a8a 0%, #3b82f6 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
text-align: center;
|
||||
margin: 1.5rem 0;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.025em;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid rgba(229, 231, 235, 0.5);
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.category-header {
|
||||
font-size: 1.4rem;
|
||||
background: linear-gradient(90deg, #2563eb 0%, #4f46e5 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
font-weight: 600;
|
||||
padding-left: 0.5rem;
|
||||
border-left: 4px solid;
|
||||
border-image: linear-gradient(to bottom, #3b82f6, #4f46e5) 1;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* Glassomorphic Tool Card Styles */
|
||||
.tool-card {
|
||||
background: linear-gradient(120deg, rgba(255, 255, 255, 0.8) 0%, rgba(240, 249, 255, 0.7) 100%);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
padding: 1.75rem;
|
||||
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
margin-bottom: 1.5rem;
|
||||
border-left: 4px solid;
|
||||
border-image: linear-gradient(to bottom, rgba(59, 130, 246, 0.8), rgba(79, 70, 229, 0.8)) 1;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tool-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 15px 30px rgba(31, 38, 135, 0.25);
|
||||
border-image: linear-gradient(to bottom, rgba(59, 130, 246, 1), rgba(79, 70, 229, 1)) 1;
|
||||
background: linear-gradient(120deg, rgba(255, 255, 255, 0.9) 0%, rgba(240, 249, 255, 0.8) 100%);
|
||||
}
|
||||
|
||||
.tool-title {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
background: linear-gradient(90deg, #1e3a8a 0%, #3b82f6 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin-bottom: 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.tool-description {
|
||||
color: #374151;
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.6;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.tool-features {
|
||||
font-size: 0.95rem;
|
||||
color: #4b5563;
|
||||
margin-bottom: 0.75rem;
|
||||
line-height: 1.6;
|
||||
background-color: rgba(243, 244, 246, 0.6);
|
||||
padding: 0.85rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(229, 231, 235, 0.5);
|
||||
}
|
||||
|
||||
/* Tool Badge Styles */
|
||||
.tool-badge {
|
||||
display: inline-block;
|
||||
padding: 0.35rem 0.75rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
margin-right: 0.5rem;
|
||||
background-color: rgba(243, 244, 246, 0.8);
|
||||
color: #4b5563;
|
||||
border: 1px solid rgba(209, 213, 219, 0.5);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.tool-badge.ai-powered {
|
||||
background-color: rgba(79, 70, 229, 0.15);
|
||||
color: #4338ca;
|
||||
border-color: rgba(79, 70, 229, 0.3);
|
||||
}
|
||||
|
||||
.tool-badge.semantic {
|
||||
background-color: rgba(16, 185, 129, 0.15);
|
||||
color: #065f46;
|
||||
border-color: rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.tool-badge.deep-learning {
|
||||
background-color: rgba(245, 158, 11, 0.15);
|
||||
color: #92400e;
|
||||
border-color: rgba(245, 158, 11, 0.3);
|
||||
}
|
||||
|
||||
.tool-badge.time-series {
|
||||
background-color: rgba(6, 182, 212, 0.15);
|
||||
color: #0e7490;
|
||||
border-color: rgba(6, 182, 212, 0.3);
|
||||
}
|
||||
|
||||
.tool-badge.forecasting {
|
||||
background-color: rgba(168, 85, 247, 0.15);
|
||||
color: #6d28d9;
|
||||
border-color: rgba(168, 85, 247, 0.3);
|
||||
}
|
||||
|
||||
.tool-badge.content-extraction {
|
||||
background-color: rgba(236, 72, 153, 0.15);
|
||||
color: #be185d;
|
||||
border-color: rgba(236, 72, 153, 0.3);
|
||||
}
|
||||
|
||||
.tool-badge.data-analysis {
|
||||
background-color: rgba(14, 165, 233, 0.15);
|
||||
color: #0369a1;
|
||||
border-color: rgba(14, 165, 233, 0.3);
|
||||
}
|
||||
|
||||
/* Glassomorphic Workflow Card Styles */
|
||||
.workflow-card {
|
||||
background: linear-gradient(135deg, rgba(239, 246, 255, 0.8) 0%, rgba(219, 234, 254, 0.7) 100%);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
padding: 1.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
border-left: 4px solid;
|
||||
border-image: linear-gradient(to bottom, rgba(37, 99, 235, 0.8), rgba(79, 70, 229, 0.8)) 1;
|
||||
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.workflow-title {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
background: linear-gradient(90deg, #1e3a8a 0%, #3b82f6 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin-bottom: 1.25rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid rgba(191, 219, 254, 0.5);
|
||||
}
|
||||
|
||||
.workflow-step-container {
|
||||
display: flex;
|
||||
margin-bottom: 1.25rem;
|
||||
background: linear-gradient(120deg, rgba(255, 255, 255, 0.7) 0%, rgba(240, 249, 255, 0.6) 100%);
|
||||
border-radius: 10px;
|
||||
padding: 1rem;
|
||||
box-shadow: 0 4px 6px rgba(31, 38, 135, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.workflow-step-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
background: linear-gradient(135deg, rgba(37, 99, 235, 0.9) 0%, rgba(79, 70, 229, 0.9) 100%);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-weight: 600;
|
||||
margin-right: 1rem;
|
||||
box-shadow: 0 4px 6px rgba(37, 99, 235, 0.3);
|
||||
}
|
||||
|
||||
.workflow-step-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-weight: 600;
|
||||
background: linear-gradient(90deg, #1e3a8a 0%, #3b82f6 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.workflow-step-description {
|
||||
color: #4b5563;
|
||||
line-height: 1.5;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* Navigation Styles */
|
||||
.nav-header {
|
||||
font-size: 2rem;
|
||||
background: linear-gradient(120deg, #1e3a8a 0%, #3b82f6 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
text-align: center;
|
||||
margin: 1.5rem 0;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.025em;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid rgba(229, 231, 235, 0.5);
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.nav-container {
|
||||
background: linear-gradient(120deg, rgba(255, 255, 255, 0.8) 0%, rgba(240, 249, 255, 0.7) 100%);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
padding: 1.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.nav-button {
|
||||
background: linear-gradient(120deg, rgba(255, 255, 255, 0.8) 0%, rgba(240, 249, 255, 0.7) 100%);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
border-left: 4px solid;
|
||||
border-image: linear-gradient(to bottom, rgba(59, 130, 246, 0.8), rgba(79, 70, 229, 0.8)) 1;
|
||||
transition: all 0.3s ease;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-button:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 15px 30px rgba(31, 38, 135, 0.25);
|
||||
border-image: linear-gradient(to bottom, rgba(59, 130, 246, 1), rgba(79, 70, 229, 1)) 1;
|
||||
background: linear-gradient(120deg, rgba(255, 255, 255, 0.9) 0%, rgba(240, 249, 255, 0.8) 100%);
|
||||
}
|
||||
|
||||
.nav-button.selected {
|
||||
background: linear-gradient(120deg, rgba(239, 246, 255, 0.9) 0%, rgba(219, 234, 254, 0.8) 100%);
|
||||
border-image: linear-gradient(to bottom, #3b82f6, #4f46e5) 1;
|
||||
box-shadow: 0 8px 20px rgba(31, 38, 135, 0.2);
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 0.75rem;
|
||||
background: linear-gradient(90deg, #1e3a8a 0%, #3b82f6 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
background: linear-gradient(90deg, #1e3a8a 0%, #3b82f6 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
[data-testid="stSidebar"] {
|
||||
background-color: rgba(248, 250, 252, 0.7);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-right: 1px solid rgba(229, 231, 235, 0.5);
|
||||
}
|
||||
|
||||
[data-testid="stSidebarNav"] {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.st-emotion-cache-16txtl3 {
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 700;
|
||||
color: #1e3a8a;
|
||||
margin: 1rem 0;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid rgba(229, 231, 235, 0.5);
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Button Styles */
|
||||
.stButton>button {
|
||||
background: linear-gradient(135deg, #3b82f6 0%, #4f46e5 100%);
|
||||
color: white;
|
||||
width: 100%;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
padding: 0.6rem 1.2rem;
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
box-shadow: 0 4px 6px rgba(31, 38, 135, 0.15);
|
||||
}
|
||||
|
||||
.stButton>button:hover {
|
||||
background: linear-gradient(135deg, #2563eb 0%, #4338ca 100%);
|
||||
box-shadow: 0 8px 15px rgba(31, 38, 135, 0.2);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Glassomorphic Results Display Styles */
|
||||
.results-container {
|
||||
background: linear-gradient(120deg, rgba(255, 255, 255, 0.8) 0%, rgba(240, 249, 255, 0.7) 100%);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
padding: 1.75rem;
|
||||
margin-top: 1.25rem;
|
||||
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.result-item {
|
||||
padding: 1.25rem;
|
||||
border-bottom: 1px solid rgba(229, 231, 235, 0.5);
|
||||
margin-bottom: 1.25rem;
|
||||
background: linear-gradient(120deg, rgba(255, 255, 255, 0.7) 0%, rgba(248, 250, 252, 0.6) 100%);
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.result-item:hover {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
box-shadow: 0 4px 6px rgba(31, 38, 135, 0.1);
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 600;
|
||||
color: #1e3a8a;
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
.result-snippet {
|
||||
color: #4b5563;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.result-url {
|
||||
color: #059669;
|
||||
font-size: 0.85rem;
|
||||
margin-top: 0.6rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* Form Styles */
|
||||
.stTextInput>div>div>input {
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(209, 213, 219, 0.5);
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.stTextInput>div>div>input:focus {
|
||||
border-color: rgba(59, 130, 246, 0.7);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
/* Dashboard Home Styles */
|
||||
.intro-container {
|
||||
background: linear-gradient(135deg, rgba(239, 246, 255, 0.8) 0%, rgba(224, 242, 254, 0.7) 100%);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
padding: 1.75rem;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
|
||||
margin-bottom: 1.5rem;
|
||||
border-left: 4px solid rgba(59, 130, 246, 0.7);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.intro-text {
|
||||
color: #374151;
|
||||
line-height: 1.7;
|
||||
font-size: 1.05rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.intro-highlight {
|
||||
color: #1e3a8a;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Chart Container Styles */
|
||||
.chart-container {
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.25rem 0;
|
||||
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: #1e3a8a;
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Tab Styles */
|
||||
.stTabs [data-baseweb="tab-list"] {
|
||||
gap: 12px;
|
||||
background: linear-gradient(120deg, rgba(255, 255, 255, 0.8) 0%, rgba(240, 249, 255, 0.7) 100%);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
padding: 1rem;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
|
||||
}
|
||||
|
||||
.stTabs [data-baseweb="tab"] {
|
||||
background: linear-gradient(120deg, rgba(255, 255, 255, 0.7) 0%, rgba(240, 249, 255, 0.6) 100%);
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
border-radius: 10px;
|
||||
padding: 0.75rem 1.25rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
border-left: 3px solid;
|
||||
border-image: linear-gradient(to bottom, rgba(59, 130, 246, 0.8), rgba(79, 70, 229, 0.8)) 1;
|
||||
transition: all 0.3s ease;
|
||||
font-weight: 500;
|
||||
color: #1e3a8a;
|
||||
}
|
||||
|
||||
.stTabs [data-baseweb="tab"]:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 15px rgba(31, 38, 135, 0.15);
|
||||
background: linear-gradient(120deg, rgba(255, 255, 255, 0.8) 0%, rgba(240, 249, 255, 0.7) 100%);
|
||||
}
|
||||
|
||||
.stTabs [aria-selected="true"] {
|
||||
background: linear-gradient(135deg, #3b82f6 0%, #4f46e5 100%) !important;
|
||||
color: white !important;
|
||||
border-image: none !important;
|
||||
border-left: 3px solid #3b82f6 !important;
|
||||
box-shadow: 0 8px 15px rgba(59, 130, 246, 0.25) !important;
|
||||
}
|
||||
|
||||
/* Additional Styles for Tabs Content */
|
||||
.stTabs [data-baseweb="tab-panel"] {
|
||||
background: linear-gradient(120deg, rgba(255, 255, 255, 0.8) 0%, rgba(240, 249, 255, 0.7) 100%);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-radius: 0 12px 12px 12px;
|
||||
padding: 1.75rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
/* Responsive Adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.tool-card, .workflow-card, .intro-container, .results-container {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.main-header {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.category-header {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.tool-title, .workflow-title {
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
}
|
||||
380
lib/alwrity_ui/alwrity_researcher/utils.py
Normal file
380
lib/alwrity_ui/alwrity_researcher/utils.py
Normal file
@@ -0,0 +1,380 @@
|
||||
import streamlit as st
|
||||
import pandas as pd
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Add parent directory to path to import modules
|
||||
sys.path.append(str(Path(__file__).parent.parent.parent))
|
||||
|
||||
# Import research modules (placeholder imports for now)
|
||||
try:
|
||||
from ai_web_researcher import (
|
||||
google_serp_search,
|
||||
tavily_ai_search,
|
||||
metaphor_basic_neural_web_search,
|
||||
google_trends_researcher,
|
||||
firecrawl_web_crawler
|
||||
)
|
||||
except ImportError:
|
||||
# For development/testing without actual modules
|
||||
pass
|
||||
|
||||
def load_css():
|
||||
"""Load custom CSS"""
|
||||
css_file = Path(__file__).parent / "style.css"
|
||||
with open(css_file) as f:
|
||||
css_content = f.read()
|
||||
# Use session state to track if CSS has been loaded
|
||||
if 'css_loaded' not in st.session_state:
|
||||
st.session_state['css_loaded'] = False
|
||||
|
||||
# Always apply CSS on each page load to ensure styles persist during navigation
|
||||
st.markdown(f"<style>{css_content}</style>", unsafe_allow_html=True)
|
||||
st.session_state['css_loaded'] = True
|
||||
|
||||
def display_google_serp_results(results):
|
||||
"""Display Google SERP search results"""
|
||||
# Check if results are available
|
||||
if not results:
|
||||
st.warning("No search results available. Please try a different query or check your API configuration.")
|
||||
return
|
||||
|
||||
st.markdown('<div class="results-container">', unsafe_allow_html=True)
|
||||
|
||||
# Display organic results
|
||||
st.markdown('<div class="category-header">Search Results</div>', unsafe_allow_html=True)
|
||||
|
||||
# Display actual organic results
|
||||
organic_results = results.get('organic', [])
|
||||
if organic_results:
|
||||
for result in organic_results:
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-title">{result.get('title', 'No Title')}</div>
|
||||
<div class="result-url">{result.get('link', '#')}</div>
|
||||
<div class="result-snippet">{result.get('snippet', 'No description available.')}</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
else:
|
||||
st.info("No organic search results found.")
|
||||
|
||||
# Display People Also Ask
|
||||
paa_results = results.get('peopleAlsoAsk', [])
|
||||
if paa_results:
|
||||
st.markdown('<div class="category-header">People Also Ask</div>', unsafe_allow_html=True)
|
||||
|
||||
for question in paa_results:
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-title">{question.get('question', 'No Question')}</div>
|
||||
<div class="result-snippet">{question.get('snippet', 'No answer available.')}</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Display Related Searches if available
|
||||
related_searches = results.get('relatedSearches', [])
|
||||
if related_searches:
|
||||
st.markdown('<div class="category-header">Related Searches</div>', unsafe_allow_html=True)
|
||||
|
||||
for search in related_searches:
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-title">{search}</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
def display_tavily_results(results):
|
||||
"""Display Tavily AI search results"""
|
||||
# Check if results are available
|
||||
if not results:
|
||||
st.warning("No Tavily search results available. Please try a different query or check your API configuration.")
|
||||
return
|
||||
|
||||
st.markdown('<div class="results-container">', unsafe_allow_html=True)
|
||||
|
||||
# Display answer if available
|
||||
answer = results.get('answer', '')
|
||||
if answer:
|
||||
st.markdown('<div class="category-header">Answer</div>', unsafe_allow_html=True)
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-snippet">{answer}</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Display search results
|
||||
search_results = results.get('results', [])
|
||||
if search_results:
|
||||
st.markdown('<div class="category-header">Search Results</div>', unsafe_allow_html=True)
|
||||
|
||||
for result in search_results:
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-title">{result.get('title', 'No Title')}</div>
|
||||
<div class="result-url">{result.get('url', '#')}</div>
|
||||
<div class="result-snippet">{result.get('content', 'No content available.')}</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
else:
|
||||
st.info("No search results found.")
|
||||
|
||||
# Display follow-up questions if available
|
||||
follow_up_questions = results.get('follow_up_questions', [])
|
||||
if follow_up_questions:
|
||||
st.markdown('<div class="category-header">Follow-up Questions</div>', unsafe_allow_html=True)
|
||||
|
||||
for question in follow_up_questions:
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-title">{question}</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
def display_metaphor_results(results):
|
||||
"""Display Metaphor Neural Search results"""
|
||||
# Check if results are available
|
||||
if not results:
|
||||
st.warning("No Metaphor search results available. Please try a different query or check your API configuration.")
|
||||
return
|
||||
|
||||
st.markdown('<div class="results-container">', unsafe_allow_html=True)
|
||||
|
||||
# Display search results
|
||||
st.markdown('<div class="category-header">Similar Content</div>', unsafe_allow_html=True)
|
||||
|
||||
# Display actual results
|
||||
documents = results.get('documents', [])
|
||||
if documents:
|
||||
for doc in documents:
|
||||
title = doc.get('title', 'No Title')
|
||||
url = doc.get('url', '#')
|
||||
extract = doc.get('extract', 'No content available.')
|
||||
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-title">{title}</div>
|
||||
<div class="result-url">{url}</div>
|
||||
<div class="result-snippet">{extract}</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
else:
|
||||
st.info("No similar content found.")
|
||||
|
||||
# Display summary if available
|
||||
summary = results.get('summary', '')
|
||||
if summary:
|
||||
st.markdown('<div class="category-header">Content Summary</div>', unsafe_allow_html=True)
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-snippet">{summary}</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
def display_google_trends_results(results):
|
||||
"""Display Google Trends results"""
|
||||
# Check if results are available
|
||||
if not results:
|
||||
st.warning("No Google Trends results available. Please try a different query or check your API configuration.")
|
||||
return
|
||||
|
||||
st.markdown('<div class="results-container">', unsafe_allow_html=True)
|
||||
|
||||
# Display interest over time chart if available
|
||||
interest_over_time = results.get('interest_over_time')
|
||||
if interest_over_time is not None and not interest_over_time.empty:
|
||||
st.markdown('<div class="category-header">Interest Over Time</div>', unsafe_allow_html=True)
|
||||
st.markdown('<div class="chart-container">', unsafe_allow_html=True)
|
||||
st.markdown('<div class="chart-title">Search Interest Over Time</div>', unsafe_allow_html=True)
|
||||
|
||||
# Convert to DataFrame if it's not already
|
||||
if not isinstance(interest_over_time, pd.DataFrame):
|
||||
interest_over_time = pd.DataFrame(interest_over_time)
|
||||
|
||||
# Prepare data for visualization
|
||||
if 'date' in interest_over_time.columns:
|
||||
# Melt the DataFrame to get it in the right format for plotting
|
||||
terms = [col for col in interest_over_time.columns if col != 'date']
|
||||
df = interest_over_time.melt('date', value_vars=terms, var_name='Term', value_name='Interest')
|
||||
|
||||
# Create and display the chart
|
||||
fig = px.line(df, x='date', y='Interest', color='Term', title='Search Interest Over Time')
|
||||
st.plotly_chart(fig, use_container_width=True)
|
||||
else:
|
||||
st.error("Interest over time data is not in the expected format.")
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
# Display related queries if available
|
||||
related_queries = results.get('related_queries', {})
|
||||
if related_queries:
|
||||
st.markdown('<div class="category-header">Related Queries</div>', unsafe_allow_html=True)
|
||||
|
||||
# Display top related queries
|
||||
for term, queries in related_queries.items():
|
||||
if 'top' in queries:
|
||||
st.markdown(f'<div class="subcategory-header">Top queries for "{term}"</div>', unsafe_allow_html=True)
|
||||
for query in queries['top'].get('query', []):
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-title">{query}</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
if 'rising' in queries:
|
||||
st.markdown(f'<div class="subcategory-header">Rising queries for "{term}"</div>', unsafe_allow_html=True)
|
||||
for query in queries['rising'].get('query', []):
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-title">{query}</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Display related topics if available
|
||||
related_topics = results.get('related_topics', {})
|
||||
if related_topics:
|
||||
st.markdown('<div class="category-header">Related Topics</div>', unsafe_allow_html=True)
|
||||
|
||||
# Display top related topics
|
||||
for term, topics in related_topics.items():
|
||||
if 'top' in topics:
|
||||
st.markdown(f'<div class="subcategory-header">Top topics for "{term}"</div>', unsafe_allow_html=True)
|
||||
for topic in topics['top'].get('topic', []):
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-title">{topic}</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
def display_crawler_results(results):
|
||||
"""Display Web Crawler results"""
|
||||
# Check if results are available
|
||||
if not results:
|
||||
st.warning("No web crawler results available. Please try a different URL or check your API configuration.")
|
||||
return
|
||||
|
||||
st.markdown('<div class="results-container">', unsafe_allow_html=True)
|
||||
|
||||
# Display crawled pages
|
||||
st.markdown('<div class="category-header">Crawled Pages</div>', unsafe_allow_html=True)
|
||||
|
||||
# Handle different result formats
|
||||
if isinstance(results, dict):
|
||||
# Single page result
|
||||
page_data = results
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-title">{page_data.get('title', 'No Title')}</div>
|
||||
<div class="result-url">{page_data.get('url', '#')}</div>
|
||||
<div class="result-snippet">
|
||||
<b>Title:</b> {page_data.get('title', 'No Title')}<br>
|
||||
<b>Description:</b> {page_data.get('description', 'No description available.')}<br>
|
||||
<b>Word Count:</b> {page_data.get('word_count', 'Unknown')}
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Display content sections if available
|
||||
content = page_data.get('content', [])
|
||||
if content:
|
||||
st.markdown('<div class="category-header">Page Content</div>', unsafe_allow_html=True)
|
||||
for section in content:
|
||||
if isinstance(section, dict):
|
||||
section_type = section.get('type', '')
|
||||
section_content = section.get('content', '')
|
||||
if section_type and section_content:
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-title">{section_type}</div>
|
||||
<div class="result-snippet">{section_content}</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
elif isinstance(results, list):
|
||||
# Multiple pages result
|
||||
for page_data in results:
|
||||
if isinstance(page_data, dict):
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-title">{page_data.get('title', 'No Title')}</div>
|
||||
<div class="result-url">{page_data.get('url', '#')}</div>
|
||||
<div class="result-snippet">
|
||||
<b>Title:</b> {page_data.get('title', 'No Title')}<br>
|
||||
<b>Description:</b> {page_data.get('description', 'No description available.')}<br>
|
||||
<b>Word Count:</b> {page_data.get('word_count', 'Unknown')}
|
||||
</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Display content structure
|
||||
st.markdown('<div class="category-header">Content Structure</div>', unsafe_allow_html=True)
|
||||
|
||||
# Placeholder data for chart
|
||||
labels = ['Blog Posts', 'Product Pages', 'Category Pages', 'About Pages', 'Contact Pages']
|
||||
values = [38, 27, 18, 10, 7]
|
||||
|
||||
fig = go.Figure(data=[go.Pie(labels=labels, values=values, hole=.3)])
|
||||
fig.update_layout(title_text='Content Type Distribution')
|
||||
st.plotly_chart(fig, use_container_width=True)
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
|
||||
def display_analyzer_results(results):
|
||||
"""Display Website Analyzer results"""
|
||||
# This is a placeholder function that will be implemented when integrated with actual modules
|
||||
st.markdown('<div class="results-container">', unsafe_allow_html=True)
|
||||
|
||||
# Display content quality metrics
|
||||
st.markdown('<div class="category-header">Content Quality Metrics</div>', unsafe_allow_html=True)
|
||||
|
||||
# Placeholder data for chart
|
||||
categories = ['Readability', 'Engagement', 'Relevance', 'Uniqueness', 'Comprehensiveness']
|
||||
values = [4.2, 3.8, 4.5, 3.9, 4.1]
|
||||
|
||||
fig = go.Figure()
|
||||
fig.add_trace(go.Scatterpolar(
|
||||
r=values,
|
||||
theta=categories,
|
||||
fill='toself',
|
||||
name='Content Quality'
|
||||
))
|
||||
fig.update_layout(
|
||||
polar=dict(
|
||||
radialaxis=dict(
|
||||
visible=True,
|
||||
range=[0, 5]
|
||||
)),
|
||||
showlegend=False
|
||||
)
|
||||
st.plotly_chart(fig, use_container_width=True)
|
||||
|
||||
# Display SEO recommendations
|
||||
st.markdown('<div class="category-header">SEO Recommendations</div>', unsafe_allow_html=True)
|
||||
|
||||
# Placeholder data
|
||||
recommendations = [
|
||||
"Improve meta descriptions for better click-through rates",
|
||||
"Add more internal links to related content",
|
||||
"Optimize images with descriptive alt text",
|
||||
"Improve page loading speed by optimizing images",
|
||||
"Add structured data markup for better search visibility"
|
||||
]
|
||||
|
||||
for recommendation in recommendations:
|
||||
st.markdown(f"""
|
||||
<div class="result-item">
|
||||
<div class="result-title">{recommendation}</div>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
st.markdown('</div>', unsafe_allow_html=True)
|
||||
Reference in New Issue
Block a user