changed to db for fit streams
This commit is contained in:
@@ -4,66 +4,7 @@
|
||||
<!-- Job Status Banner -->
|
||||
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Last Sync Status</h5>
|
||||
<div class="table-responsive" style="max-height: 300px; overflow-y: auto;">
|
||||
<table class="table table-sm" id="metrics-status-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Source</th>
|
||||
<th>Found</th>
|
||||
<th>Synced</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="4">No sync data available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mt-2 text-muted small">
|
||||
<span id="db-stats"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Sync Controls</h5>
|
||||
<div class="d-grid gap-2">
|
||||
<button class="btn btn-primary" type="button" id="sync-activities-btn">Sync Latest
|
||||
Activities (30d)</button>
|
||||
<button class="btn btn-outline-primary" type="button" id="sync-all-activities-btn">Sync All
|
||||
Historical Activities</button>
|
||||
<hr>
|
||||
<h6 class="text-muted">Health Metrics</h6>
|
||||
<button class="btn btn-info text-white" type="button" id="scan-health-btn">Scan Health Gaps
|
||||
(30d)</button>
|
||||
<button class="btn btn-outline-info" type="button" id="sync-pending-health-btn">Sync Pending
|
||||
Health Metrics</button>
|
||||
<hr>
|
||||
<h6 class="text-muted">Fitbit Sync</h6>
|
||||
<button class="btn btn-success" type="button" id="sync-fitbit-btn">Sync Latest Weight
|
||||
(Fitbit) (30d)</button>
|
||||
<button class="btn btn-outline-success" type="button" id="sync-all-fitbit-btn">Sync All
|
||||
Historical Weight (Fitbit)</button>
|
||||
<button class="btn btn-warning mt-2" type="button" id="compare-fitbit-btn">Compare Fitbit vs
|
||||
Garmin</button>
|
||||
<hr>
|
||||
<h6 class="text-muted">Debug</h6>
|
||||
<button class="btn btn-outline-secondary" type="button" id="test-queue-btn"
|
||||
title="Simulate 5s Job"><i class="bi bi-bug"></i> Test Queue (5s)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Scheduled Jobs Row -->
|
||||
<div class="row mb-4">
|
||||
@@ -72,6 +13,8 @@
|
||||
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||||
<h5 class="card-title mb-0"><i class="bi bi-calendar-check"></i> Scheduled Jobs</h5>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-warning me-2" onclick="resetJobsToDefaults()"><i
|
||||
class="bi bi-arrow-counterclockwise"></i> Reset Defaults</button>
|
||||
<button class="btn btn-sm btn-success me-2" onclick="createModal.show()"><i
|
||||
class="bi bi-plus-lg"></i> Add Job</button>
|
||||
<button class="btn btn-sm btn-light" onclick="loadJobs()"><i class="bi bi-arrow-clockwise"></i>
|
||||
@@ -373,15 +316,7 @@
|
||||
loadDashboardData(); // Refresh data when job finishes
|
||||
});
|
||||
|
||||
document.getElementById('sync-activities-btn').addEventListener('click', () => syncActivities(30));
|
||||
document.getElementById('sync-all-activities-btn').addEventListener('click', () => syncActivities(3650));
|
||||
|
||||
document.getElementById('scan-health-btn').addEventListener('click', scanHealth);
|
||||
document.getElementById('sync-pending-health-btn').addEventListener('click', syncPendingHealth);
|
||||
|
||||
document.getElementById('sync-fitbit-btn').addEventListener('click', () => syncFitbitWeight('30d'));
|
||||
document.getElementById('sync-all-fitbit-btn').addEventListener('click', () => syncFitbitWeight('all'));
|
||||
document.getElementById('compare-fitbit-btn').addEventListener('click', compareWeight);
|
||||
|
||||
const testBtn = document.getElementById('test-queue-btn');
|
||||
if (testBtn) {
|
||||
@@ -448,25 +383,7 @@
|
||||
dbStats.innerHTML = `<strong>DB Total Activities:</strong> ${data.total_activities} | <strong>Downloaded:</strong> ${data.downloaded_activities}`;
|
||||
}
|
||||
|
||||
const metricsBody = document.querySelector('#metrics-status-table tbody');
|
||||
if (metricsBody) {
|
||||
metricsBody.innerHTML = '';
|
||||
|
||||
if (data.last_sync_stats && data.last_sync_stats.length > 0) {
|
||||
data.last_sync_stats.forEach(stat => {
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>${stat.type}</td>
|
||||
<td>${stat.source}</td>
|
||||
<td>${stat.total}</td>
|
||||
<td class="${stat.synced > 0 ? 'text-success' : ''}">${stat.synced}</td>
|
||||
`;
|
||||
metricsBody.appendChild(row);
|
||||
});
|
||||
} else {
|
||||
metricsBody.innerHTML = '<tr><td colspan="4" class="text-center text-muted">No detailed sync stats available. Run a sync to populate.</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
const logsBody = document.querySelector('#sync-logs-table tbody');
|
||||
if (logsBody) {
|
||||
@@ -671,140 +588,23 @@
|
||||
});
|
||||
}
|
||||
|
||||
async function syncActivities(daysBack = 30) {
|
||||
const typeLabel = daysBack > 1000 ? 'Historical' : 'Latest';
|
||||
showToast(`${typeLabel} Syncing...`, `Starting ${typeLabel} Activity sync (Scan + Download)...`, 'info');
|
||||
|
||||
|
||||
async function resetJobsToDefaults() {
|
||||
if (!confirm("Are you sure? This will delete all current jobs and restore system defaults.")) return;
|
||||
try {
|
||||
// Step 1: Scan
|
||||
const scanRes = await fetch(`/api/activities/sync/scan?days_back=${daysBack}`, { method: 'POST' });
|
||||
if (!scanRes.ok) {
|
||||
const err = await scanRes.json();
|
||||
throw new Error(err.detail || "Scan failed");
|
||||
}
|
||||
const scanData = await scanRes.json();
|
||||
showToast('Scan Started', `Job ID: ${scanData.job_id} (Scanning)`, 'success');
|
||||
|
||||
// Helper to poll
|
||||
const poll = async (jid) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const check = async () => {
|
||||
try {
|
||||
const r = await fetch(`/api/jobs/${jid}`);
|
||||
if (r.status === 404) return resolve('done');
|
||||
const d = await r.json();
|
||||
if (d.status === 'completed') resolve('done');
|
||||
else if (d.status === 'failed') reject(d.message);
|
||||
else setTimeout(check, 2000);
|
||||
} catch (e) { reject(e); }
|
||||
};
|
||||
check();
|
||||
});
|
||||
};
|
||||
|
||||
await poll(scanData.job_id);
|
||||
|
||||
// Step 2: Sync Pending
|
||||
const syncRes = await fetch('/api/activities/sync/pending', { method: 'POST' });
|
||||
if (!syncRes.ok) {
|
||||
const err = await syncRes.json();
|
||||
throw new Error(err.detail || "Download failed");
|
||||
}
|
||||
const syncData = await syncRes.json();
|
||||
showToast('Download Started', `Job ID: ${syncData.job_id} (Downloading)`, 'success');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error syncing activities:', error);
|
||||
showToast('Sync Error', `Activity sync failed: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function scanHealth() {
|
||||
showToast('Scan Started', 'Scanning for health data gaps...', 'info');
|
||||
try {
|
||||
const response = await fetch('/api/metrics/sync/scan', { method: 'POST' });
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
showToast('Scan Started', `Job ID: ${data.job_id}`, 'success');
|
||||
const res = await fetch('/api/scheduling/jobs/reset-defaults', { method: 'POST' });
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
showToast('Reset Complete', data.message, 'success');
|
||||
loadJobs();
|
||||
} else {
|
||||
showToast('Error', 'Failed to start scan', 'error');
|
||||
showToast('Error', data.detail || "Failed to reset", 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error scanning health:', error);
|
||||
showToast('Error', error.message, 'error');
|
||||
}
|
||||
} catch (e) { showToast('Error', e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function syncPendingHealth() {
|
||||
showToast('Sync Started', 'Syncing pending health metrics...', 'info');
|
||||
try {
|
||||
const response = await fetch('/api/metrics/sync/pending', { method: 'POST' });
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
showToast('Sync Started', `Job ID: ${data.job_id}`, 'success');
|
||||
} else {
|
||||
showToast('Error', 'Failed to start sync', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error syncing health:', error);
|
||||
showToast('Error', error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function syncFitbitWeight(scope) {
|
||||
const typeLabel = scope === 'all' ? 'All History' : 'Latest (30d)';
|
||||
showToast(`Fitbit Syncing...`, `Fitbit Weight sync initiated (${typeLabel}).`, 'info');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/sync/fitbit/weight', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ scope: scope })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.detail || errorData.message || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
showToast('Fitbit Sync Complete', data.message, 'success');
|
||||
loadDashboardData();
|
||||
} catch (error) {
|
||||
console.error('Error syncing Fitbit weight:', error);
|
||||
showToast('Fitbit Sync Error', `Sync failed: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function compareWeight() {
|
||||
showToast('Comparing...', 'Comparing Fitbit and Garmin weight records...', 'info');
|
||||
try {
|
||||
const response = await fetch('/api/sync/compare-weight', { method: 'POST' });
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.detail || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
showToast('Comparison Results',
|
||||
`Fitbit Total: ${data.fitbit_total}\n` +
|
||||
`Garmin Total: ${data.garmin_total}\n` +
|
||||
`Missing in Garmin: ${data.missing_in_garmin}\n` +
|
||||
`${data.message}`,
|
||||
data.missing_in_garmin > 0 ? 'warning' : 'success'
|
||||
);
|
||||
|
||||
console.log("Comparison Data:", data);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error comparing weight:', error);
|
||||
showToast('Comparison Error', `Comparison failed: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// --- Scheduler Functions ---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user