Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Settings | Stock Scanner</title> | |
| <link rel="stylesheet" href="/static/css/common.css"> | |
| </head> | |
| <body> | |
| <nav class="main-nav" id="main-nav"></nav> | |
| <div class="page-wrapper"> | |
| <div class="control-panel"> | |
| <div class="input-row"> | |
| <div class="card-title">Hugging Face Cloud</div> | |
| <div id="hf-status" class="badge badge-neutral">Checking...</div> | |
| </div> | |
| <div class="card-subtitle" style="margin: var(--space-sm) 0;"> | |
| <strong>Repository:</strong> <span id="repo-name">Loading...</span> · | |
| <strong>Last Sync:</strong> <span id="last-sync">Loading...</span> | |
| </div> | |
| <div class="action-row"> | |
| <button id="btn-sync" class="btn btn-primary">🔄 Update Dataset</button> | |
| <button id="btn-push-config" class="btn">☁️ Save Config</button> | |
| </div> | |
| <div id="log-container"></div> | |
| </div> | |
| <div class="control-panel"> | |
| <div class="card-title">Project Info</div> | |
| <div class="card-subtitle">Environment details</div> | |
| <div id="env-info" class="text-dim" style="margin-top: var(--space-sm); font-size: var(--font-sm); line-height: 1.6;"> | |
| Loading... | |
| </div> | |
| </div> | |
| </div> | |
| <style> | |
| #log-container { | |
| background: var(--bg); | |
| color: var(--positive); | |
| font-family: 'Courier New', monospace; | |
| padding: var(--space-md); | |
| border-radius: 4px; | |
| font-size: var(--font-sm); | |
| height: 200px; | |
| overflow-y: auto; | |
| margin-top: var(--space-md); | |
| display: none; | |
| border: 1px solid var(--border); | |
| } | |
| .pulse { | |
| animation: pulse-animation 2s infinite; | |
| } | |
| @keyframes pulse-animation { | |
| 0% { transform: scale(1); opacity: 1; } | |
| 50% { transform: scale(1.05); opacity: 0.7; } | |
| 100% { transform: scale(1); opacity: 1; } | |
| } | |
| </style> | |
| <script> | |
| const statusEl = document.getElementById('hf-status'); | |
| const repoEl = document.getElementById('repo-name'); | |
| const lastSyncEl = document.getElementById('last-sync'); | |
| const envEl = document.getElementById('env-info'); | |
| const btnSync = document.getElementById('btn-sync'); | |
| const btnPush = document.getElementById('btn-push-config'); | |
| const logContainer = document.getElementById('log-container'); | |
| async function fetchStatus() { | |
| try { | |
| const resp = await fetch('/api/hf/status'); | |
| const data = await resp.json(); | |
| repoEl.textContent = data.repo; | |
| lastSyncEl.textContent = new Date(data.last_sync).toLocaleString(); | |
| if (data.token_set) { | |
| statusEl.textContent = 'Connected to Cloud'; | |
| statusEl.className = 'badge badge-positive'; | |
| } else { | |
| statusEl.textContent = 'Token Not Set (Read-Only)'; | |
| statusEl.className = 'badge badge-negative'; | |
| btnSync.disabled = true; | |
| btnPush.disabled = true; | |
| } | |
| } catch (err) { | |
| statusEl.textContent = 'API Error'; | |
| statusEl.className = 'badge badge-negative'; | |
| } | |
| } | |
| async function fetchEnv() { | |
| try { | |
| const resp = await fetch('/api/test'); | |
| const data = await resp.json(); | |
| envEl.innerHTML = ` | |
| <strong>Space ID:</strong> ${data.env.SPACE_ID || 'Local'}<br> | |
| <strong>Python:</strong> ${data.env.PYTHON_VERSION}<br> | |
| <strong>HF Token:</strong> ${data.env.HF_TOKEN} | |
| `; | |
| } catch (err) { | |
| envEl.textContent = 'Could not fetch environment info.'; | |
| } | |
| } | |
| function appendLog(msg) { | |
| logContainer.style.display = 'block'; | |
| const line = document.createElement('div'); | |
| line.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`; | |
| logContainer.appendChild(line); | |
| logContainer.scrollTop = logContainer.scrollHeight; | |
| } | |
| btnSync.onclick = async () => { | |
| if (!confirm('This will fetch fresh data from Alpaca, rebuild the dataset, and push it to Hugging Face. This may take several minutes. Continue?')) return; | |
| btnSync.disabled = true; | |
| btnSync.classList.add('pulse'); | |
| appendLog('Triggering full synchronization...'); | |
| try { | |
| const resp = await fetch('/api/hf/sync', { method: 'POST' }); | |
| const data = await resp.json(); | |
| appendLog('Background process started. Monitor HF Dataset for progress.'); | |
| alert('Sync process started in the background. It will update the HF Dataset once complete.'); | |
| } catch (err) { | |
| appendLog('Error starting sync: ' + err.message); | |
| btnSync.disabled = false; | |
| } | |
| }; | |
| btnPush.onclick = async () => { | |
| btnPush.disabled = true; | |
| appendLog('Pushing watchlist and filters to cloud...'); | |
| try { | |
| const resp = await fetch('/api/hf/push-config', { method: 'POST' }); | |
| const data = await resp.json(); | |
| if (data.success) { | |
| appendLog('Config pushed successfully.'); | |
| alert('Configuration saved to cloud.'); | |
| } else { | |
| appendLog('Push failed. Check logs.'); | |
| } | |
| } catch (err) { | |
| appendLog('Error: ' + err.message); | |
| } finally { | |
| btnPush.disabled = false; | |
| } | |
| }; | |
| fetchStatus(); | |
| fetchEnv(); | |
| </script> | |
| <script src="/static/js/nav.js"></script> | |
| </body> | |
| </html> | |