CoolFace
Apppublic

harshkumar27/data-analysis-agent

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
index.html298 linesDownload Raw Back to root
1<!DOCTYPE html>2<html lang="en">3<head>4  <meta charset="UTF-8"/>5  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>6  <title>TDS Data Analyst Agent</title>7  <style>8  * { margin:0; padding:0; box-sizing:border-box; }9  body {10    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;11    line-height: 1.6; color:#f5f5f5;12    background: linear-gradient(135deg,#1e1e2f 0%,#2a2a3d 100%);13    min-height:100vh;14  }15  .container { max-width:1200px; margin:0 auto; padding:20px; }16  .header { text-align:center; margin-bottom:30px; color:#fff; }17  .header h1 { font-size:2.5rem; margin-bottom:10px; text-shadow:2px 2px 4px rgba(0,0,0,.6); }18  .header p { font-size:1.1rem; opacity:.85; }19  .main-card {20    background:#2c2c3e; border-radius:15px; box-shadow:0 10px 30px rgba(0,0,0,.4);21    padding:30px; margin-bottom:30px;22  }23  .form-group label { display:block; margin-bottom:8px; font-weight:600; color:#ddd; }24  .file-input {25    width:100%; padding:15px; border:2px dashed #555; border-radius:8px;26    background:#1f1f2e; cursor:pointer; transition:.3s ease; text-align:center; position:relative;27    color:#bbb;28  }29  .file-input:hover { border-color:#667eea; background:#2a2a40; color:#fff; }30  .file-info { margin-top:10px; padding:10px; background:#333; border-radius:5px; font-size:14px; color:#a8c7ff; display:none; }31  .btn {32    background:linear-gradient(135deg,#667eea 0%,#764ba2 100%); color:#fff; border:none;33    padding:15px 30px; border-radius:8px; font-size:16px; font-weight:600; cursor:pointer; transition:.3s ease; width:100%;34  }35  .btn:hover { transform:translateY(-2px); box-shadow:0 5px 15px rgba(102,126,234,.6); }36  .btn:disabled { background:#555; cursor:not-allowed; transform:none; box-shadow:none; }37  .btn-secondary { background:#444; color:#ddd; }38  .button-group { display:flex; gap:10px; margin-top:20px; flex-wrap:wrap; }39  .loading { display:none; text-align:center; padding:20px; }40  .spinner {41    border:4px solid #444; border-top:4px solid #667eea; border-radius:50%;42    width:40px; height:40px; animation:spin 1s linear infinite; margin:0 auto 15px;43  }44  @keyframes spin { 0%{transform:rotate(0)} 100%{transform:rotate(360deg)} }45  .results { display:none; margin-top:30px; }46  .result-item { background:#1f1f2e; border-left:4px solid #667eea; padding:20px; margin-bottom:20px; border-radius:0 8px 8px 0; }47  .question { font-weight:600; color:#fff; margin-bottom:15px; font-size:1.1rem; }48  .answer { color:#ccc; line-height:1.6; }49  .answer pre {50    background:#2c2c3e; color:#eee;51  }52  .answer img {53    max-width:100%; height:auto; border-radius:8px; margin:10px 0;54    box-shadow:0 4px 8px rgba(0,0,0,.5); cursor:pointer; transition:transform .3s ease;55  }56  .answer img:hover { transform:scale(1.02); }57  .error { background:#3b1f1f; border-left-color:#dc3545; color:#ffb3b3; }58  .modal { display:none; position:fixed; z-index:1000; left:0; top:0; width:100%; height:100%; background:rgba(0,0,0,.9); }59  .modal-content { margin:auto; display:block; width:80%; max-width:700px; max-height:80%; object-fit:contain; }60  .close { position:absolute; top:15px; right:35px; color:#f1f1f1; font-size:40px; font-weight:bold; cursor:pointer; }61  .close:hover { color:#bbb; }62  @media (max-width:768px){63    .container{padding:10px}64    .header h1{font-size:2rem}65    .main-card{padding:20px}66    .button-group{flex-direction:column}67    .btn-secondary{width:100%}68  }69</style>70</head>71<body>72  <div class="container">73    <div class="header">74      <h1>๐Ÿค– TDS Data Analyst Agent</h1>75      <p>Upload your questions file and optional dataset to get intelligent answers with visualizations</p>76    </div>77 78    <div class="main-card">79      <form id="analysisForm">80        <!-- Questions file (REQUIRED) -->81        <div class="form-group">82          <label for="questions_file">Questions File (.txt) <span style="color:#dc3545">*</span></label>83          <div class="file-input-wrapper">84            <div class="file-input" id="questionsDrop">85              <input type="file" id="questions_file" name="questions_file" accept=".txt"/>86              <span>๐Ÿ“ Click to upload your questions (.txt)</span>87            </div>88          </div>89          <div id="questionsInfo" class="file-info"></div>90        </div>91 92        <!-- Dataset file (OPTIONAL) -->93        <div class="form-group">94          <label for="data_file">Upload Dataset (Optional)</label>95          <div class="file-input-wrapper">96            <div class="file-input" id="dataDrop">97              <input type="file" id="data_file" name="data_file" accept=".csv,.xlsx,.xls,.json,.parquet,.txt"/>98              <span>๐Ÿ“ Click or drag & drop your dataset</span>99            </div>100          </div>101          <div id="dataInfo" class="file-info"></div>102        </div>103 104        <div class="button-group">105          <button type="submit" class="btn" id="submitBtn">๐Ÿš€ Analyze Data</button>106          <button type="button" class="btn btn-secondary" id="clearBtn">๐Ÿ—‘๏ธ Clear</button>107        </div>108      </form>109 110      <div class="loading" id="loading">111        <div class="spinner"></div>112        <p>Analyzing your data... This may take a few moments.</p>113      </div>114 115      <div class="results" id="results">116        <h3>๐Ÿ“Š Analysis Results</h3>117        <div id="resultsContent"></div>118      </div>119    </div>120  </div>121 122  <!-- Image Modal -->123  <div id="imageModal" class="modal">124    <span class="close">&times;</span>125    <img class="modal-content" id="modalImage" alt="Visualization"/>126  </div>127 128  <script>129    /* ---------- Image normalization helpers ---------- */130    function normalizeImageData(answer) {131      let s = null;132      if (typeof answer === 'string') { s = answer.trim(); }133      else if (answer && typeof answer === 'object') {134        s = answer.image || answer.base64 || answer.plot || answer.data || null;135        if (typeof s === 'string') s = s.trim();136      }137      if (!s) return null;138 139      s = s.replace(/^data:aimage\//i, 'data:image/');140 141      if (/^data:image\//i.test(s)) return s;142 143      const base64Like = /^[A-Za-z0-9+/=\r\n]+$/.test(s) && s.length > 200 && !/\s{2,}/.test(s);144      if (base64Like) return 'data:image/png;base64,' + s;145 146      return null;147    }148 149    function renderAnswerInto(answer, container, onImageClick) {150      const maybeImg = normalizeImageData(answer);151      if (maybeImg) {152        const img = document.createElement('img');153        img.src = maybeImg;154        img.alt = 'Generated visualization';155        img.addEventListener('click', () => onImageClick(maybeImg));156        container.appendChild(img);157        return;158      }159      const text = (answer && typeof answer === 'object') ? JSON.stringify(answer, null, 2) : String(answer ?? '');160      if (text.includes('\n') || text.length > 120) {161        const pre = document.createElement('pre');162        pre.style.whiteSpace = 'pre-wrap';163        pre.style.background = '#f8f9fa';164        pre.style.padding = '15px';165        pre.style.borderRadius = '5px';166        pre.style.overflow = 'auto';167        pre.textContent = text;168        container.appendChild(pre);169      } else {170        container.textContent = text;171      }172    }173 174    /* ---------------- App ---------------- */175    class DataAnalystApp {176      constructor() {177        this.form = document.getElementById('analysisForm');178        this.qFileInput = document.getElementById('questions_file');179        this.dFileInput = document.getElementById('data_file');180        this.qDrop = document.getElementById('questionsDrop');181        this.dDrop = document.getElementById('dataDrop');182        this.qInfo = document.getElementById('questionsInfo');183        this.dInfo = document.getElementById('dataInfo');184        this.submitBtn = document.getElementById('submitBtn');185        this.clearBtn = document.getElementById('clearBtn');186        this.loading = document.getElementById('loading');187        this.results = document.getElementById('results');188        this.resultsContent = document.getElementById('resultsContent');189        this.modal = document.getElementById('imageModal');190        this.modalImage = document.getElementById('modalImage');191        this.closeModal = document.querySelector('.close');192        this.initEventListeners();193      }194 195      initEventListeners() {196        this.form.addEventListener('submit', (e) => this.handleSubmit(e));197        this.clearBtn.addEventListener('click', () => this.clearForm());198        this.qFileInput.addEventListener('change', (e) => this.handleFileSelect(e, 'q'));199        this.dFileInput.addEventListener('change', (e) => this.handleFileSelect(e, 'd'));200        this.closeModal.addEventListener('click', () => this.hideImageModal());201        this.modal.addEventListener('click', (e) => { if (e.target === this.modal) this.hideImageModal(); });202        this.setupDrop(this.dDrop, (files) => { if (files && files.length > 0) { this.dFileInput.files = files; this.handleFileSelect({ target: { files } }, 'd'); } });203        this.setupDrop(this.qDrop, (files) => { if (files && files.length > 0) { this.qFileInput.files = files; this.handleFileSelect({ target: { files } }, 'q'); } });204      }205 206      setupDrop(zone, onDropFiles) {207        zone.addEventListener('dragover', (e) => { e.preventDefault(); zone.style.borderColor = '#667eea'; });208        zone.addEventListener('dragleave', (e) => { e.preventDefault(); zone.style.borderColor = '#e1e5e9'; });209        zone.addEventListener('drop', (e) => { e.preventDefault(); zone.style.borderColor = '#e1e5e9'; onDropFiles(e.dataTransfer.files); });210      }211 212      handleFileSelect(event, type) {213        const file = event.target.files[0];214        if (type === 'q') {215          if (file) { this.qInfo.innerHTML = `<strong>Questions:</strong> ${file.name} (${(file.size / 1024).toFixed(2)} KB)`; this.qInfo.style.display = 'block'; }216          else { this.qInfo.style.display = 'none'; }217        } else {218          if (file) { this.dInfo.innerHTML = `<strong>Dataset:</strong> ${file.name} (${(file.size / 1024).toFixed(2)} KB)`; this.dInfo.style.display = 'block'; }219          else { this.dInfo.style.display = 'none'; }220        }221      }222 223      async handleSubmit(event) {224        event.preventDefault();225        if (!this.qFileInput.files[0]) { alert('Please upload your questions .txt file.'); return; }226        this.showLoading(); this.hideResults();227 228        try {229          const formData = new FormData();230          formData.append('questions_file', this.qFileInput.files[0]);231          if (this.dFileInput.files[0]) formData.append('data_file', this.dFileInput.files[0]);232 233          const response = await fetch('/api', { method:'POST', body:formData }); // changed endpoint234 235          if (!response.ok) {236            let msg = `HTTP ${response.status}`;237            try { const err = await response.json(); if (err && err.detail) msg = err.detail; } catch {}238            throw new Error(msg);239          }240 241          const data = await response.json();242          this.displayResults(data);243        } catch (err) {244          console.error('Analysis failed:', err);245          this.displayError(err.message || 'Unknown error');246        } finally {247          this.hideLoading();248        }249      }250 251      displayResults(data) {252        this.resultsContent.innerHTML = '';253        if (data.error) { this.displayError(data.error); return; }254        Object.entries(data).forEach(([question, answer]) => {255          const item = document.createElement('div');256          item.className = 'result-item';257          const qDiv = document.createElement('div');258          qDiv.className = 'question';259          qDiv.textContent = question;260          const aDiv = document.createElement('div');261          aDiv.className = 'answer';262          renderAnswerInto(answer, aDiv, (src) => this.showImageModal(src));263          item.appendChild(qDiv); item.appendChild(aDiv);264          this.resultsContent.appendChild(item);265        });266        this.showResults(); this.scrollToResults();267      }268 269      displayError(message) {270        this.resultsContent.innerHTML = '';271        const item = document.createElement('div');272        item.className = 'result-item error';273        const title = document.createElement('div');274        title.className = 'question';275        title.textContent = 'โŒ Error';276        const content = document.createElement('div');277        content.className = 'answer';278        content.textContent = message;279        item.appendChild(title); item.appendChild(content);280        this.resultsContent.appendChild(item);281        this.showResults(); this.scrollToResults();282      }283 284      showImageModal(src) { this.modalImage.src = src; this.modal.style.display = 'block'; document.body.style.overflow = 'hidden'; }285      hideImageModal() { this.modal.style.display = 'none'; document.body.style.overflow = 'auto'; }286      showLoading() { this.loading.style.display = 'block'; this.submitBtn.disabled = true; this.submitBtn.textContent = 'โณ Analyzing...'; }287      hideLoading() { this.loading.style.display = 'none'; this.submitBtn.disabled = false; this.submitBtn.textContent = '๐Ÿš€ Analyze Data'; }288      showResults() { this.results.style.display = 'block'; }289      hideResults() { this.results.style.display = 'none'; }290      scrollToResults() { setTimeout(() => this.results.scrollIntoView({ behavior:'smooth', block:'start' }), 100); }291      clearForm() { this.qFileInput.value = ''; this.dFileInput.value = ''; this.qInfo.style.display = 'none'; this.dInfo.style.display = 'none'; this.hideResults(); this.hideLoading(); }292    }293 294    document.addEventListener('DOMContentLoaded', () => { new DataAnalystApp(); });295  </script>296</body>297</html>298