BI_ANALYTICS / web_interface.py
ratulsur's picture
Upload 8 files
e51a81b verified
"""
BI Storyteller Web Interface
Professional HTTP server with REST API - Standard Library Only
"""
import json
import os
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
import mimetypes
from main import BIStoryteller
class BIStoryteller_WebHandler(BaseHTTPRequestHandler):
"""HTTP request handler for BI Storyteller web interface"""
def __init__(self, *args, **kwargs):
self.bi = BIStoryteller()
super().__init__(*args, **kwargs)
def do_GET(self):
"""Handle GET requests"""
if self.path == '/' or self.path == '/index.html':
self.serve_main_page()
elif self.path.startswith('/api/status'):
self.get_status()
else:
self.send_error(404, "Page not found")
def do_POST(self):
"""Handle POST requests"""
content_length = int(self.headers.get('Content-Length', 0))
post_data = self.rfile.read(content_length)
try:
data = json.loads(post_data.decode('utf-8'))
except:
self.send_error(400, "Invalid JSON")
return
if self.path == '/api/set_api_key':
result = self.bi.set_groq_api_key(data.get('api_key', ''))
self._send_json_response(result)
elif self.path == '/api/extract_variables':
result = self.bi.extract_variables(data.get('business_problem', ''))
self._send_json_response(result)
elif self.path == '/api/generate_questionnaire':
result = self.bi.generate_questionnaire(
data.get('variables', []),
data.get('business_problem', '')
)
self._send_json_response(result)
elif self.path == '/api/generate_data':
result = self.bi.generate_sample_data(
data.get('variables', []),
data.get('sample_size', 1000)
)
self._send_json_response(result)
elif self.path == '/api/clean_data':
result = self.bi.clean_data(data.get('data', []))
self._send_json_response(result)
elif self.path == '/api/perform_eda':
result = self.bi.perform_eda(data.get('data', []))
self._send_json_response(result)
elif self.path == '/api/train_model':
result = self.bi.train_predictive_model(
data.get('data', []),
data.get('algorithm', 'Random Forest')
)
self._send_json_response(result)
elif self.path == '/api/analyze_trends':
result = self.bi.analyze_trends(
data.get('data', []),
data.get('time_period', 'Monthly')
)
self._send_json_response(result)
elif self.path == '/api/analyze_sentiment':
result = self.bi.analyze_sentiment(data.get('data', []))
self._send_json_response(result)
elif self.path == '/api/run_ab_test':
result = self.bi.run_ab_test(
data.get('data', []),
data.get('test_variable', ''),
data.get('success_metric', '')
)
self._send_json_response(result)
elif self.path == '/api/chat':
result = self.bi.chat_with_data(data.get('question', ''))
self._send_json_response(result)
elif self.path == '/api/export':
result = self.bi.export_results(data.get('filename'))
self._send_json_response(result)
elif self.path == '/api/import':
result = self.bi.import_results(data.get('filename'))
self._send_json_response(result)
else:
self.send_error(404, "API endpoint not found")
def get_status(self):
"""Get current analysis status"""
status = {
"modules_completed": [],
"current_step": 1,
"total_steps": 12
}
if self.bi.variables:
status["modules_completed"].append("Variable Extraction")
status["current_step"] = 2
if self.bi.questionnaire:
status["modules_completed"].append("Questionnaire Generation")
status["current_step"] = 3
if self.bi.sample_data:
status["modules_completed"].append("Data Generation")
status["current_step"] = 4
if self.bi.cleaned_data:
status["modules_completed"].append("Data Cleaning")
status["current_step"] = 5
if self.bi.eda_results:
status["modules_completed"].append("EDA Analysis")
status["current_step"] = 6
if self.bi.model_results:
status["modules_completed"].append("Predictive Modeling")
status["current_step"] = 7
if self.bi.trend_results:
status["modules_completed"].append("Trend Analysis")
status["current_step"] = 8
if self.bi.sentiment_results:
status["modules_completed"].append("Sentiment Analysis")
status["current_step"] = 9
if self.bi.ab_test_results:
status["modules_completed"].append("A/B Testing")
status["current_step"] = 10
if self.bi.chat_history:
status["modules_completed"].append("Chat Interface")
status["current_step"] = 11
self._send_json_response(status)
def serve_main_page(self):
"""Serve the main HTML page"""
html_content = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BI Storyteller - Marketing Analysis Platform</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
color: white;
margin-bottom: 30px;
}
.header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
font-weight: 700;
}
.header p {
font-size: 1.2rem;
opacity: 0.9;
}
.workflow-container {
background: white;
border-radius: 20px;
padding: 30px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
}
.progress-bar {
width: 100%;
height: 8px;
background: #e0e0e0;
border-radius: 4px;
margin-bottom: 30px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #667eea, #764ba2);
width: 8.33%;
transition: width 0.3s ease;
}
.module {
border: 2px solid #e0e0e0;
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
transition: all 0.3s ease;
}
.module.active {
border-color: #667eea;
background: #f8f9ff;
}
.module.completed {
border-color: #4caf50;
background: #f1f8e9;
}
.module h3 {
color: #333;
margin-bottom: 15px;
font-size: 1.3rem;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 600;
color: #555;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 16px;
transition: border-color 0.3s ease;
}
.form-group input:focus,
.form-group textarea:focus,
.form-group select:focus {
outline: none;
border-color: #667eea;
}
.btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s ease;
}
.btn:hover {
transform: translateY(-2px);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.results {
background: #f8f9fa;
border-radius: 8px;
padding: 15px;
margin-top: 15px;
border-left: 4px solid #667eea;
}
.results pre {
white-space: pre-wrap;
font-family: 'Monaco', 'Consolas', monospace;
font-size: 14px;
line-height: 1.4;
}
.status {
text-align: center;
padding: 10px;
margin-bottom: 20px;
border-radius: 8px;
font-weight: 600;
}
.status.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.hidden {
display: none;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
@media (max-width: 768px) {
.container {
padding: 10px;
}
.header h1 {
font-size: 2rem;
}
.workflow-container {
padding: 20px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>πŸš€ BI Storyteller</h1>
<p>Marketing Analysis Automation Platform</p>
</div>
<div class="workflow-container">
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
</div>
<div id="statusMessage" class="status hidden"></div>
<!-- Module 1: API Key Setup -->
<div class="module active" id="module1">
<h3>πŸ”‘ Step 1: API Key Setup (Optional)</h3>
<div class="form-group">
<label for="apiKey">Groq API Key (Leave empty for offline mode):</label>
<input type="password" id="apiKey" placeholder="Enter your Groq API key...">
</div>
<button class="btn" onclick="setApiKey()">Set API Key & Continue</button>
</div>
<!-- Module 2: Variable Extraction -->
<div class="module" id="module2">
<h3>πŸ“ Step 2: Variable Extraction</h3>
<div class="form-group">
<label for="businessProblem">Describe Your Business Problem:</label>
<textarea id="businessProblem" rows="4" placeholder="e.g., We want to improve customer retention and increase purchase frequency..."></textarea>
</div>
<button class="btn" onclick="extractVariables()">Extract Variables</button>
<div id="variablesResult" class="results hidden"></div>
</div>
<!-- Module 3: Questionnaire Generation -->
<div class="module" id="module3">
<h3>πŸ“‹ Step 3: Generate Questionnaire</h3>
<p>Generate survey questions based on extracted variables.</p>
<button class="btn" onclick="generateQuestionnaire()">Generate Questionnaire</button>
<div id="questionnaireResult" class="results hidden"></div>
</div>
<!-- Module 4: Data Generation -->
<div class="module" id="module4">
<h3>πŸ”’ Step 4: Generate Sample Data</h3>
<div class="form-group">
<label for="sampleSize">Sample Size:</label>
<select id="sampleSize">
<option value="100">100 records</option>
<option value="500" selected>500 records</option>
<option value="1000">1,000 records</option>
<option value="5000">5,000 records</option>
</select>
</div>
<button class="btn" onclick="generateData()">Generate Sample Data</button>
<div id="dataResult" class="results hidden"></div>
</div>
<!-- Module 5: Data Cleaning -->
<div class="module" id="module5">
<h3>🧹 Step 5: Clean Data</h3>
<p>Remove outliers, handle missing values, and preprocess data.</p>
<button class="btn" onclick="cleanData()">Clean Data</button>
<div id="cleaningResult" class="results hidden"></div>
</div>
<!-- Module 6: EDA -->
<div class="module" id="module6">
<h3>πŸ“Š Step 6: Exploratory Data Analysis</h3>
<p>Perform statistical analysis and generate insights.</p>
<button class="btn" onclick="performEDA()">Perform EDA</button>
<div id="edaResult" class="results hidden"></div>
</div>
<!-- Module 7: Predictive Analytics -->
<div class="module" id="module7">
<h3>πŸ€– Step 7: Predictive Analytics</h3>
<div class="form-group">
<label for="algorithm">Algorithm:</label>
<select id="algorithm">
<option value="Random Forest">Random Forest</option>
<option value="Logistic Regression">Logistic Regression</option>
<option value="SVM">Support Vector Machine</option>
<option value="Neural Network">Neural Network</option>
</select>
</div>
<button class="btn" onclick="trainModel()">Train Model</button>
<div id="modelResult" class="results hidden"></div>
</div>
<!-- Module 8: Trend Analysis -->
<div class="module" id="module8">
<h3>πŸ“ˆ Step 8: Trend Analysis</h3>
<div class="form-group">
<label for="timePeriod">Time Period:</label>
<select id="timePeriod">
<option value="Daily">Daily</option>
<option value="Weekly">Weekly</option>
<option value="Monthly" selected>Monthly</option>
</select>
</div>
<button class="btn" onclick="analyzeTrends()">Analyze Trends</button>
<div id="trendResult" class="results hidden"></div>
</div>
<!-- Module 9: Sentiment Analysis -->
<div class="module" id="module9">
<h3>πŸ’­ Step 9: Sentiment Analysis</h3>
<p>Analyze customer feedback and sentiment patterns.</p>
<button class="btn" onclick="analyzeSentiment()">Analyze Sentiment</button>
<div id="sentimentResult" class="results hidden"></div>
</div>
<!-- Module 10: A/B Testing -->
<div class="module" id="module10">
<h3>πŸ§ͺ Step 10: A/B Testing</h3>
<div class="grid">
<div class="form-group">
<label for="testVariable">Test Variable:</label>
<input type="text" id="testVariable" placeholder="e.g., channel">
</div>
<div class="form-group">
<label for="successMetric">Success Metric:</label>
<input type="text" id="successMetric" placeholder="e.g., conversion_rate">
</div>
</div>
<button class="btn" onclick="runABTest()">Run A/B Test</button>
<div id="abTestResult" class="results hidden"></div>
</div>
<!-- Module 11: Chat Interface -->
<div class="module" id="module11">
<h3>πŸ’¬ Step 11: Chat with Your Data</h3>
<div class="form-group">
<label for="chatQuestion">Ask a Question:</label>
<input type="text" id="chatQuestion" placeholder="e.g., What are the key factors driving customer satisfaction?">
</div>
<button class="btn" onclick="chatWithData()">Ask Question</button>
<div id="chatResult" class="results hidden"></div>
</div>
<!-- Module 12: Export -->
<div class="module" id="module12">
<h3>πŸ“€ Step 12: Export Results</h3>
<div class="grid">
<div>
<button class="btn" onclick="exportResults()">Export Analysis (JSON)</button>
</div>
<div>
<button class="btn" onclick="exportCSV('cleaned')">Export Cleaned Data (CSV)</button>
</div>
</div>
<div id="exportResult" class="results hidden"></div>
</div>
</div>
</div>
<script>
let currentStep = 1;
let analysisData = {};
function updateProgress() {
const progressFill = document.getElementById('progressFill');
const percentage = (currentStep / 12) * 100;
progressFill.style.width = percentage + '%';
}
function showStatus(message, isError = false) {
const statusEl = document.getElementById('statusMessage');
statusEl.textContent = message;
statusEl.className = `status ${isError ? 'error' : 'success'}`;
statusEl.classList.remove('hidden');
setTimeout(() => statusEl.classList.add('hidden'), 5000);
}
function activateModule(moduleNum) {
// Deactivate all modules
document.querySelectorAll('.module').forEach(m => {
m.classList.remove('active');
});
// Mark completed modules
for (let i = 1; i < moduleNum; i++) {
document.getElementById(`module${i}`).classList.add('completed');
}
// Activate current module
document.getElementById(`module${moduleNum}`).classList.add('active');
currentStep = moduleNum;
updateProgress();
}
async function apiCall(endpoint, data = {}) {
try {
const response = await fetch(`/api/${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
return await response.json();
} catch (error) {
console.error('API call failed:', error);
return { success: false, error: error.message };
}
}
async function setApiKey() {
const apiKey = document.getElementById('apiKey').value;
const result = await apiCall('set_api_key', { api_key: apiKey });
if (result.success) {
showStatus(result.message);
activateModule(2);
} else {
showStatus(result.error || 'Failed to set API key', true);
}
}
async function extractVariables() {
const businessProblem = document.getElementById('businessProblem').value;
if (!businessProblem.trim()) {
showStatus('Please describe your business problem', true);
return;
}
const result = await apiCall('extract_variables', { business_problem: businessProblem });
if (result.success) {
analysisData.variables = result.variables;
analysisData.businessProblem = businessProblem;
document.getElementById('variablesResult').innerHTML = `
<pre>βœ… Extracted Variables:
${result.variables.map(v => `β€’ ${v.replace('_', ' ')}`).join('\\n')}
Extraction Method: ${result.extraction_method}
Confidence: ${(result.confidence * 100).toFixed(1)}%</pre>
`;
document.getElementById('variablesResult').classList.remove('hidden');
showStatus(`Successfully extracted ${result.variables.length} variables`);
activateModule(3);
} else {
showStatus(result.error || 'Failed to extract variables', true);
}
}
async function generateQuestionnaire() {
if (!analysisData.variables) {
showStatus('Please extract variables first', true);
return;
}
const result = await apiCall('generate_questionnaire', {
variables: analysisData.variables,
business_problem: analysisData.businessProblem
});
if (result.success) {
analysisData.questionnaire = result.questionnaire;
document.getElementById('questionnaireResult').innerHTML = `
<pre>βœ… Generated Questionnaire:
Total Questions: ${result.total_questions}
Estimated Time: ${result.estimated_time}
Sample Questions:
${result.questionnaire.slice(0, 3).map((q, i) => `${i+1}. ${q.question}`).join('\\n')}</pre>
`;
document.getElementById('questionnaireResult').classList.remove('hidden');
showStatus(`Generated ${result.total_questions} survey questions`);
activateModule(4);
} else {
showStatus(result.error || 'Failed to generate questionnaire', true);
}
}
async function generateData() {
if (!analysisData.variables) {
showStatus('Please extract variables first', true);
return;
}
const sampleSize = parseInt(document.getElementById('sampleSize').value);
const result = await apiCall('generate_data', {
variables: analysisData.variables,
sample_size: sampleSize
});
if (result.success) {
analysisData.sampleData = result.data;
document.getElementById('dataResult').innerHTML = `
<pre>βœ… Sample Data Generated:
Records: ${result.sample_size}
Variables: ${result.variables.length}
Method: ${result.generation_method}
Sample Record:
${JSON.stringify(result.data[0], null, 2)}</pre>
`;
document.getElementById('dataResult').classList.remove('hidden');
showStatus(`Generated ${result.sample_size} sample records`);
activateModule(5);
} else {
showStatus(result.error || 'Failed to generate data', true);
}
}
async function cleanData() {
if (!analysisData.sampleData) {
showStatus('Please generate sample data first', true);
return;
}
const result = await apiCall('clean_data', { data: analysisData.sampleData });
if (result.success) {
analysisData.cleanedData = result.cleaned_data;
document.getElementById('cleaningResult').innerHTML = `
<pre>βœ… Data Cleaning Complete:
Original Records: ${result.original_size}
Cleaned Records: ${result.cleaned_size}
Outliers Removed: ${result.removed_outliers}
Data Quality: ${((result.cleaned_size / result.original_size) * 100).toFixed(1)}%</pre>
`;
document.getElementById('cleaningResult').classList.remove('hidden');
showStatus(`Data cleaned: ${result.cleaned_size} records ready for analysis`);
activateModule(6);
} else {
showStatus(result.error || 'Failed to clean data', true);
}
}
async function performEDA() {
if (!analysisData.cleanedData) {
showStatus('Please clean data first', true);
return;
}
const result = await apiCall('perform_eda', { data: analysisData.cleanedData });
if (result.success) {
analysisData.edaResults = result.results;
const correlations = Object.entries(result.results.correlations || {})
.map(([pair, corr]) => `${pair}: ${corr}`)
.slice(0, 5)
.join('\\n');
document.getElementById('edaResult').innerHTML = `
<pre>βœ… EDA Analysis Complete:
Variables Analyzed: ${Object.keys(result.results.descriptive_stats || {}).length}
Key Insights: ${result.results.insights.length}
Top Correlations:
${correlations}
Key Insights:
${result.results.insights.map(insight => `β€’ ${insight}`).join('\\n')}</pre>
`;
document.getElementById('edaResult').classList.remove('hidden');
showStatus('EDA analysis completed with key insights generated');
activateModule(7);
} else {
showStatus(result.error || 'Failed to perform EDA', true);
}
}
async function trainModel() {
if (!analysisData.cleanedData) {
showStatus('Please clean data first', true);
return;
}
const algorithm = document.getElementById('algorithm').value;
const result = await apiCall('train_model', {
data: analysisData.cleanedData,
algorithm: algorithm
});
if (result.success) {
analysisData.modelResults = result.results;
const featureImportance = Object.entries(result.results.feature_importance || {})
.sort(([,a], [,b]) => b - a)
.slice(0, 5)
.map(([feature, importance]) => `${feature}: ${(importance * 100).toFixed(1)}%`)
.join('\\n');
document.getElementById('modelResult').innerHTML = `
<pre>βœ… Predictive Model Trained:
Algorithm: ${result.results.algorithm}
Accuracy: ${(result.results.metrics.accuracy * 100).toFixed(1)}%
Precision: ${(result.results.metrics.precision * 100).toFixed(1)}%
Recall: ${(result.results.metrics.recall * 100).toFixed(1)}%
Top Feature Importance:
${featureImportance}</pre>
`;
document.getElementById('modelResult').classList.remove('hidden');
showStatus(`Model trained with ${(result.results.metrics.accuracy * 100).toFixed(1)}% accuracy`);
activateModule(8);
} else {
showStatus(result.error || 'Failed to train model', true);
}
}
async function analyzeTrends() {
if (!analysisData.cleanedData) {
showStatus('Please clean data first', true);
return;
}
const timePeriod = document.getElementById('timePeriod').value;
const result = await apiCall('analyze_trends', {
data: analysisData.cleanedData,
time_period: timePeriod
});
if (result.success) {
analysisData.trendResults = result.results;
const trends = Object.entries(result.results.trends || {})
.map(([variable, trend]) => `${variable}: ${trend.direction} (slope: ${trend.slope})`)
.slice(0, 5)
.join('\\n');
document.getElementById('trendResult').innerHTML = `
<pre>βœ… Trend Analysis Complete:
Time Period: ${result.results.time_period}
Analysis Periods: ${result.results.analysis_periods}
Key Trends:
${trends}</pre>
`;
document.getElementById('trendResult').classList.remove('hidden');
showStatus('Trend analysis completed with forecasts generated');
activateModule(9);
} else {
showStatus(result.error || 'Failed to analyze trends', true);
}
}
async function analyzeSentiment() {
if (!analysisData.cleanedData) {
showStatus('Please clean data first', true);
return;
}
const result = await apiCall('analyze_sentiment', { data: analysisData.cleanedData });
if (result.success) {
analysisData.sentimentResults = result.results;
const distribution = Object.entries(result.results.sentiment_distribution || {})
.map(([sentiment, percentage]) => `${sentiment}: ${percentage}%`)
.join('\\n');
document.getElementById('sentimentResult').innerHTML = `
<pre>βœ… Sentiment Analysis Complete:
Total Responses Analyzed: ${result.results.total_analyzed}
Dominant Sentiment: ${result.results.dominant_sentiment}
Sentiment Distribution:
${distribution}
Analysis Method: ${result.results.analysis_method}</pre>
`;
document.getElementById('sentimentResult').classList.remove('hidden');
showStatus(`Sentiment analysis: ${result.results.dominant_sentiment} sentiment dominates`);
activateModule(10);
} else {
showStatus(result.error || 'Failed to analyze sentiment', true);
}
}
async function runABTest() {
if (!analysisData.cleanedData) {
showStatus('Please clean data first', true);
return;
}
const testVariable = document.getElementById('testVariable').value;
const successMetric = document.getElementById('successMetric').value;
if (!testVariable || !successMetric) {
showStatus('Please specify both test variable and success metric', true);
return;
}
const result = await apiCall('run_ab_test', {
data: analysisData.cleanedData,
test_variable: testVariable,
success_metric: successMetric
});
if (result.success) {
analysisData.abTestResults = result.results;
document.getElementById('abTestResult').innerHTML = `
<pre>βœ… A/B Test Analysis Complete:
Group A: ${result.results.group_a.size} users, ${(result.results.group_a.success_rate * 100).toFixed(1)}% success rate
Group B: ${result.results.group_b.size} users, ${(result.results.group_b.success_rate * 100).toFixed(1)}% success rate
Statistical Test:
Z-Score: ${result.results.statistical_test.z_score}
P-Value: ${result.results.statistical_test.p_value}
Significant: ${result.results.statistical_test.is_significant ? 'Yes' : 'No'}
Conclusion: ${result.results.conclusion.winner}
Lift: ${result.results.conclusion.lift}%</pre>
`;
document.getElementById('abTestResult').classList.remove('hidden');
showStatus(`A/B test completed: ${result.results.conclusion.winner}`);
activateModule(11);
} else {
showStatus(result.error || 'Failed to run A/B test', true);
}
}
async function chatWithData() {
const question = document.getElementById('chatQuestion').value;
if (!question.trim()) {
showStatus('Please enter a question', true);
return;
}
const result = await apiCall('chat', { question: question });
if (result.success) {
document.getElementById('chatResult').innerHTML = `
<pre>❓ Question: ${question}
πŸ€– Response: ${result.response}
Context Used: ${result.context_used} analysis modules</pre>
`;
document.getElementById('chatResult').classList.remove('hidden');
document.getElementById('chatQuestion').value = '';
showStatus('Chat response generated');
activateModule(12);
} else {
showStatus(result.error || 'Failed to get chat response', true);
}
}
async function exportResults() {
const result = await apiCall('export', {});
if (result.success) {
document.getElementById('exportResult').innerHTML = `
<pre>βœ… Analysis Exported Successfully:
Filename: ${result.filename}
Modules Completed: ${result.modules_completed}
File Size: ${(result.file_size / 1024).toFixed(1)} KB
Your complete analysis has been saved and can be shared or imported later.</pre>
`;
document.getElementById('exportResult').classList.remove('hidden');
showStatus(`Analysis exported to ${result.filename}`);
} else {
showStatus(result.error || 'Failed to export results', true);
}
}
async function exportCSV(dataType) {
const result = await apiCall('export_csv', { data_type: dataType });
if (result.success) {
showStatus(`${dataType} data exported to ${result.filename}`);
} else {
showStatus(result.error || 'Failed to export CSV', true);
}
}
// Initialize
updateProgress();
</script>
</body>
</html>
"""
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(html_content.encode())
def _send_json_response(self, data):
"""Send JSON response"""
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
self.wfile.write(json.dumps(data).encode())
def log_message(self, format, *args):
"""Override to reduce log noise"""
pass
def start_web_server(port=8000):
"""Start the web server"""
server_address = ('', port)
httpd = HTTPServer(server_address, BIStoryteller_WebHandler)
print(f"🌐 BI Storyteller Web Interface Starting...")
print(f"πŸ“ Server running at: http://localhost:{port}")
print(f"πŸ”§ Standard Library Only - No External Dependencies")
print(f"⚑ Ready for Marketing Analysis Automation!")
print("\n" + "="*50)
print("Press Ctrl+C to stop the server")
print("="*50)
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\nπŸ›‘ Server stopped by user")
httpd.server_close()
if __name__ == "__main__":
start_web_server()