feat(reports): manual FTP "Download & Save" saves a parsed session

ftp-download-folder-to-server and ftp-download-to-server now route NRL data through the shared ingest (ingest_nrl_zip / _ingest_file_entries) instead of hand-rolling DataFile rows on a now/zero-duration session. Folder save requires the unit be assigned to a location; non-NRL single files keep the generic save path. The FTP browser popup now reports how long the measurement ran.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 18:12:15 +00:00
parent 2ecf1f54d5
commit 7716a4b51d
2 changed files with 171 additions and 261 deletions
+21 -3
View File
@@ -542,8 +542,11 @@ async function downloadFolderToServer(unitId, remotePath, folderName) {
const data = await response.json();
if (response.ok) {
// Show success message
alert(`✓ Folder "${folderName}" downloaded successfully!\n\n${data.file_count} files extracted\nTotal size: ${formatFileSize(data.total_size)}\n\nFiles are now available in the Project Files section below.`);
// Show success message — surface how long the measurement ran
alert(`✓ Folder "${folderName}" saved!\n\n` +
(data.message || `${data.file_count} file(s) imported`) +
formatRunLength(data) +
`\n\nNow saved as a session in the Project Files section below.`);
// Refresh the unified files list
htmx.trigger('#unified-files', 'refresh');
@@ -585,7 +588,11 @@ async function downloadToServer(unitId, remotePath, fileName) {
if (response.ok) {
// Show success message
alert(`${fileName} downloaded to server successfully!\n\nFile ID: ${data.file_id}\nSize: ${formatFileSize(data.file_size)}`);
const sizeLine = `\nSize: ${formatFileSize(data.file_size)}`;
const msg = data.ingested
? `${fileName} imported as measurement data!` + formatRunLength(data) + sizeLine
: `${fileName} downloaded to server successfully!\n\nFile ID: ${data.file_id}` + sizeLine;
alert(msg);
// Refresh the unified files list
htmx.trigger('#unified-files', 'refresh');
@@ -607,6 +614,17 @@ function formatFileSize(bytes) {
return (bytes / 1073741824).toFixed(2) + ' GB';
}
// Build a "how long did it run" line from an ingest response. Duration is
// timezone-independent (stop start), so it's the reliable number to show.
function formatRunLength(data) {
if (data.duration_seconds == null) return '';
const s = data.duration_seconds;
const h = Math.floor(s / 3600);
const m = Math.floor((s % 3600) / 60);
let txt = h > 0 ? `${h}h ${m}m` : `${m}m`;
return `\n\nRecorded for: ${txt}`;
}
// Check FTP status for all units on load
// Use setTimeout to ensure DOM elements exist when HTMX loads this partial
setTimeout(function() {