Download script.js from nolanzandi/virtual-data-analyst: direct link, hf CLI and curl.
- Browser
- Download file 15.4 kB
-
https://huggingface.co/spaces/nolanzandi/virtual-data-analyst/resolve/refs%2Fpr%2F21/script.js
- Command line
-
hf download hf://spaces/nolanzandi/virtual-data-analyst@refs/pr/21/script.js
-
curl -L -o script.js https://huggingface.co/spaces/nolanzandi/virtual-data-analyst/resolve/refs%2Fpr%2F21/script.js
15.4 kB
| // API Configuration | |
| const API_URL = 'https://nolanzandi-virtual-data-analyst.hf.space'; | |
| const PREDICT_ENDPOINT = `${API_URL}/api/predict`; | |
| const SAMPLE_ENDPOINT = `${API_URL}/api/sample`; | |
| // File Upload and API Integration | |
| async function handleFileUpload(file) { | |
| try { | |
| // Show loading state | |
| document.querySelector('.upload-icon').style.display = 'none'; | |
| document.querySelector('.loading-spinner').style.display = 'block'; | |
| document.querySelector('.progress-bar').style.display = 'block'; | |
| // Create FormData | |
| const formData = new FormData(); | |
| formData.append('file', file); | |
| // Update file info | |
| updateFileInfo(file); | |
| // Simulate progress while actually uploading | |
| const progressInterval = simulateProgress(); | |
| // Make API request | |
| const response = await fetch(API_URL, { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| if (!response.ok) { | |
| throw new Error('API request failed'); | |
| } | |
| const data = await response.json(); | |
| // Clear progress simulation | |
| clearInterval(progressInterval); | |
| // Show success state | |
| showSuccessState(); | |
| // Handle API response | |
| handleApiResponse(data); | |
| } catch (error) { | |
| console.error('Error:', error); | |
| showErrorState(error.message); | |
| } | |
| } | |
| function updateFileInfo(file) { | |
| const fileInfo = document.getElementById('fileInfo'); | |
| const fileName = fileInfo.querySelector('.file-name'); | |
| const fileSize = fileInfo.querySelector('.file-size'); | |
| const fileIcon = fileInfo.querySelector('.file-type-icon'); | |
| const fileType = file.name.split('.').pop().toLowerCase(); | |
| const iconClass = getFileTypeIcon(fileType); | |
| fileIcon.className = `file-type-icon fas ${iconClass}`; | |
| fileName.textContent = file.name; | |
| fileSize.textContent = formatFileSize(file.size); | |
| fileInfo.classList.remove('hidden'); | |
| } | |
| function simulateProgress() { | |
| const progressBar = document.querySelector('.progress-bar-fill'); | |
| let progress = 0; | |
| return setInterval(() => { | |
| if (progress < 90) { // Only go up to 90% until we get actual completion | |
| progress += 5; | |
| progressBar.style.width = `${progress}%`; | |
| } | |
| }, 100); | |
| } | |
| function showSuccessState() { | |
| document.querySelector('.loading-spinner').style.display = 'none'; | |
| document.querySelector('.success-checkmark').style.display = 'block'; | |
| document.querySelector('.progress-bar-fill').style.width = '100%'; | |
| setTimeout(() => { | |
| resetUploadState(); | |
| }, 2000); | |
| } | |
| function showErrorState(message) { | |
| // Reset upload UI | |
| resetUploadState(); | |
| // Show error message | |
| const errorDiv = document.createElement('div'); | |
| errorDiv.className = 'text-red-500 mt-4'; | |
| errorDiv.innerHTML = `<i class="fas fa-exclamation-circle mr-2"></i>${message}`; | |
| document.querySelector('.drop-zone').appendChild(errorDiv); | |
| setTimeout(() => { | |
| errorDiv.remove(); | |
| }, 5000); | |
| } | |
| function resetUploadState() { | |
| document.querySelector('.success-checkmark').style.display = 'none'; | |
| document.querySelector('.upload-icon').style.display = 'block'; | |
| document.querySelector('.progress-bar').style.display = 'none'; | |
| document.querySelector('.progress-bar-fill').style.width = '0%'; | |
| document.getElementById('fileInfo').classList.add('hidden'); | |
| } | |
| function handleSampleDataClick(datasetName) { | |
| // Show loading state in results section | |
| const resultsSection = document.getElementById('results'); | |
| const resultsLoading = document.getElementById('resultsLoading'); | |
| const resultsContent = document.getElementById('resultsContent'); | |
| const resultsError = document.getElementById('resultsError'); | |
| resultsSection.classList.remove('hidden'); | |
| resultsLoading.classList.remove('hidden'); | |
| resultsContent.classList.add('hidden'); | |
| resultsError.classList.add('hidden'); | |
| // Simulate API delay | |
| setTimeout(() => { | |
| try { | |
| // Mock data based on dataset type | |
| const mockData = datasetName === 'marketing_campaign' ? { | |
| statistics: { | |
| rows: 10000, | |
| columns: 15, | |
| missing_values: 120, | |
| data_types: ['numeric', 'categorical', 'datetime'] | |
| }, | |
| preview: { | |
| columns: ['Campaign ID', 'Customer ID', 'Response', 'Channel'], | |
| data: [ | |
| ['CAM001', 'C001', 'Converted', 'Email'], | |
| ['CAM001', 'C002', 'No Response', 'SMS'], | |
| ['CAM002', 'C003', 'Converted', 'Social Media'] | |
| ] | |
| }, | |
| visualizations: [ | |
| { | |
| title: 'Response Rate by Channel', | |
| description: 'Conversion rates across different marketing channels', | |
| image_url: 'https://via.placeholder.com/400x300' | |
| }, | |
| { | |
| title: 'Campaign Performance', | |
| description: 'Success metrics for each campaign', | |
| image_url: 'https://via.placeholder.com/400x300' | |
| } | |
| ], | |
| insights: [ | |
| { | |
| title: 'Best Performing Channel', | |
| description: 'Email campaigns show highest conversion rate at 28%' | |
| }, | |
| { | |
| title: 'Optimal Send Time', | |
| description: 'Campaigns sent between 2 PM - 4 PM have better engagement' | |
| } | |
| ] | |
| } : { | |
| statistics: { | |
| rows: 50000, | |
| columns: 12, | |
| missing_values: 85, | |
| data_types: ['numeric', 'categorical', 'datetime'] | |
| }, | |
| preview: { | |
| columns: ['Order ID', 'Product', 'Quantity', 'Price'], | |
| data: [ | |
| ['ORD001', 'Laptop', '1', '$999.99'], | |
| ['ORD002', 'Mouse', '2', '$29.99'], | |
| ['ORD003', 'Monitor', '1', '$299.99'] | |
| ] | |
| }, | |
| visualizations: [ | |
| { | |
| title: 'Sales by Category', | |
| description: 'Distribution of sales across product categories', | |
| image_url: 'https://via.placeholder.com/400x300' | |
| }, | |
| { | |
| title: 'Monthly Revenue', | |
| description: 'Revenue trends over the past 12 months', | |
| image_url: 'https://via.placeholder.com/400x300' | |
| } | |
| ], | |
| insights: [ | |
| { | |
| title: 'Top Products', | |
| description: 'Electronics category generates 45% of total revenue' | |
| }, | |
| { | |
| title: 'Customer Behavior', | |
| description: 'Average order value increased by 15% in Q4' | |
| } | |
| ] | |
| }; | |
| handleApiResponse(mockData); | |
| } catch (error) { | |
| console.error('Error:', error); | |
| showErrorState('Failed to process sample dataset'); | |
| } | |
| }, 1000); // 1 second delay to show loading state | |
| } | |
| function handleApiResponse(data) { | |
| const resultsSection = document.getElementById('results'); | |
| const resultsLoading = document.getElementById('resultsLoading'); | |
| const resultsContent = document.getElementById('resultsContent'); | |
| const resultsError = document.getElementById('resultsError'); | |
| // Show results section | |
| resultsSection.classList.remove('hidden'); | |
| resultsLoading.classList.add('hidden'); | |
| resultsError.classList.add('hidden'); | |
| resultsContent.classList.remove('hidden'); | |
| // Update Basic Statistics | |
| updateBasicStats(data.statistics); | |
| // Update Data Preview | |
| updateDataPreview(data.preview); | |
| // Update Visualizations | |
| updateVisualizations(data.visualizations); | |
| // Update Insights | |
| updateInsights(data.insights); | |
| } | |
| function updateBasicStats(statistics) { | |
| const statsContainer = document.getElementById('basicStats'); | |
| statsContainer.innerHTML = ''; | |
| const stats = [ | |
| { label: 'Rows', value: statistics.rows, icon: 'fa-list' }, | |
| { label: 'Columns', value: statistics.columns, icon: 'fa-columns' }, | |
| { label: 'Missing Values', value: statistics.missing_values, icon: 'fa-exclamation-triangle' }, | |
| { label: 'Data Types', value: statistics.data_types.length, icon: 'fa-code' } | |
| ]; | |
| stats.forEach(stat => { | |
| const statDiv = document.createElement('div'); | |
| statDiv.className = 'bg-gray-50 rounded-lg p-4'; | |
| statDiv.innerHTML = ` | |
| <div class="flex items-center"> | |
| <i class="fas ${stat.icon} text-primary text-xl mr-3"></i> | |
| <div> | |
| <div class="text-sm text-gray-500">${stat.label}</div> | |
| <div class="text-lg font-semibold">${stat.value}</div> | |
| </div> | |
| </div> | |
| `; | |
| statsContainer.appendChild(statDiv); | |
| }); | |
| } | |
| function updateDataPreview(preview) { | |
| const table = document.getElementById('dataPreview'); | |
| table.innerHTML = ''; | |
| // Add header | |
| const thead = document.createElement('thead'); | |
| thead.className = 'bg-gray-50'; | |
| const headerRow = document.createElement('tr'); | |
| preview.columns.forEach(column => { | |
| const th = document.createElement('th'); | |
| th.className = 'px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider'; | |
| th.textContent = column; | |
| headerRow.appendChild(th); | |
| }); | |
| thead.appendChild(headerRow); | |
| table.appendChild(thead); | |
| // Add body | |
| const tbody = document.createElement('tbody'); | |
| tbody.className = 'bg-white divide-y divide-gray-200'; | |
| preview.data.forEach(row => { | |
| const tr = document.createElement('tr'); | |
| row.forEach(cell => { | |
| const td = document.createElement('td'); | |
| td.className = 'px-6 py-4 whitespace-nowrap text-sm text-gray-500'; | |
| td.textContent = cell; | |
| tr.appendChild(td); | |
| }); | |
| tbody.appendChild(tr); | |
| }); | |
| table.appendChild(tbody); | |
| } | |
| function updateVisualizations(visualizations) { | |
| const container = document.getElementById('visualizations'); | |
| container.innerHTML = ''; | |
| visualizations.forEach(viz => { | |
| const vizDiv = document.createElement('div'); | |
| vizDiv.className = 'bg-white rounded-lg p-4 shadow'; | |
| vizDiv.innerHTML = ` | |
| <h4 class="text-lg font-medium text-gray-800 mb-4">${viz.title}</h4> | |
| <div class="aspect-w-16 aspect-h-9"> | |
| <img src="${viz.image_url}" alt="${viz.title}" class="rounded-lg"> | |
| </div> | |
| <p class="mt-2 text-sm text-gray-600">${viz.description}</p> | |
| `; | |
| container.appendChild(vizDiv); | |
| }); | |
| } | |
| function updateInsights(insights) { | |
| const insightsList = document.getElementById('insights'); | |
| insightsList.innerHTML = ''; | |
| insights.forEach(insight => { | |
| const li = document.createElement('li'); | |
| li.className = 'bg-blue-50 rounded-lg p-4'; | |
| li.innerHTML = ` | |
| <div class="flex items-start"> | |
| <i class="fas fa-lightbulb text-yellow-500 mt-1 mr-3"></i> | |
| <div> | |
| <div class="font-medium text-blue-900">${insight.title}</div> | |
| <p class="mt-1 text-sm text-blue-700">${insight.description}</p> | |
| </div> | |
| </div> | |
| `; | |
| insightsList.appendChild(li); | |
| }); | |
| } | |
| function closeResults() { | |
| document.getElementById('results').classList.add('hidden'); | |
| } | |
| function showErrorState(message) { | |
| const resultsSection = document.getElementById('results'); | |
| const resultsLoading = document.getElementById('resultsLoading'); | |
| const resultsContent = document.getElementById('resultsContent'); | |
| const resultsError = document.getElementById('resultsError'); | |
| const errorMessage = document.getElementById('errorMessage'); | |
| resultsSection.classList.remove('hidden'); | |
| resultsLoading.classList.add('hidden'); | |
| resultsContent.classList.add('hidden'); | |
| resultsError.classList.remove('hidden'); | |
| errorMessage.textContent = message; | |
| } | |
| // Event Listeners | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // File Upload Handling | |
| const dropZone = document.querySelector('.drop-zone'); | |
| const fileInput = document.getElementById('fileInput'); | |
| // Prevent default drag behaviors | |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { | |
| dropZone.addEventListener(eventName, preventDefaults, false); | |
| document.body.addEventListener(eventName, preventDefaults, false); | |
| }); | |
| // Highlight drop zone when dragging over it | |
| ['dragenter', 'dragover'].forEach(eventName => { | |
| dropZone.addEventListener(eventName, highlight, false); | |
| }); | |
| ['dragleave', 'drop'].forEach(eventName => { | |
| dropZone.addEventListener(eventName, unhighlight, false); | |
| }); | |
| // Handle dropped files | |
| dropZone.addEventListener('drop', (e) => { | |
| const dt = e.dataTransfer; | |
| const files = dt.files; | |
| if (files.length > 0) { | |
| handleFileUpload(files[0]); | |
| } | |
| }); | |
| fileInput.addEventListener('change', (e) => { | |
| if (e.target.files.length > 0) { | |
| handleFileUpload(e.target.files[0]); | |
| } | |
| }); | |
| // Sample Data Button Handlers | |
| const marketingBtn = document.querySelector('.sample-btn:nth-child(1)'); | |
| const retailBtn = document.querySelector('.sample-btn:nth-child(2)'); | |
| if (marketingBtn) { | |
| marketingBtn.addEventListener('click', () => { | |
| console.log('Marketing campaign button clicked'); | |
| handleSampleDataClick('marketing_campaign'); | |
| }); | |
| } | |
| if (retailBtn) { | |
| retailBtn.addEventListener('click', () => { | |
| console.log('Online retail button clicked'); | |
| handleSampleDataClick('online_retail'); | |
| }); | |
| } | |
| }); | |
| // Utility Functions | |
| function preventDefaults(e) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| } | |
| function highlight(e) { | |
| document.querySelector('.drop-zone').classList.add('border-primary', 'bg-blue-50'); | |
| } | |
| function unhighlight(e) { | |
| document.querySelector('.drop-zone').classList.remove('border-primary', 'bg-blue-50'); | |
| } | |
| function getFileTypeIcon(fileType) { | |
| const icons = { | |
| 'csv': 'fa-file-csv', | |
| 'tsv': 'fa-file-alt', | |
| 'txt': 'fa-file-alt', | |
| 'xls': 'fa-file-excel', | |
| 'xlsx': 'fa-file-excel', | |
| 'xml': 'fa-file-code', | |
| 'json': 'fa-file-code' | |
| }; | |
| return icons[fileType] || 'fa-file'; | |
| } | |
| function formatFileSize(bytes) { | |
| if (bytes === 0) return '0 Bytes'; | |
| const k = 1024; | |
| const sizes = ['Bytes', 'KB', 'MB', 'GB']; | |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); | |
| return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; | |
| } | |