fix: Auto-downloaded files now show up in project_files data.

This commit is contained in:
serversdwn
2026-02-18 19:51:44 +00:00
parent 80861997af
commit 28942600ab

View File

@@ -295,9 +295,20 @@ class SchedulerService:
stop_cycle handles:
1. Stop measurement
2. Enable FTP
3. Download measurement folder
4. Verify download
3. Download measurement folder to SLMM local storage
After stop_cycle, if download succeeded, this method fetches the ZIP
from SLMM and extracts it into Terra-View's project directory, creating
DataFile records for each file.
"""
import hashlib
import io
import os
import zipfile
import httpx
from pathlib import Path
from backend.models import DataFile
# Parse notes for download preference
include_download = True
try:
@@ -308,7 +319,7 @@ class SchedulerService:
pass # Notes is plain text, not JSON
# Execute the full stop cycle via device controller
# SLMM handles stop, FTP enable, and download
# SLMM handles stop, FTP enable, and download to SLMM-local storage
cycle_response = await self.device_controller.stop_cycle(
unit_id,
action.device_type,
@@ -340,10 +351,81 @@ class SchedulerService:
except json.JSONDecodeError:
pass
db.commit()
# If SLMM downloaded the folder successfully, fetch the ZIP from SLMM
# and extract it into Terra-View's project directory, creating DataFile records
files_created = 0
if include_download and cycle_response.get("download_success") and active_session:
folder_name = cycle_response.get("downloaded_folder") # e.g. "Auto_0058"
remote_path = f"/NL-43/{folder_name}"
try:
SLMM_BASE_URL = os.getenv("SLMM_BASE_URL", "http://localhost:8100")
async with httpx.AsyncClient(timeout=600.0) as client:
zip_response = await client.post(
f"{SLMM_BASE_URL}/api/nl43/{unit_id}/ftp/download-folder",
json={"remote_path": remote_path}
)
if zip_response.is_success and len(zip_response.content) > 22:
base_dir = Path(f"data/Projects/{action.project_id}/{active_session.id}/{folder_name}")
base_dir.mkdir(parents=True, exist_ok=True)
file_type_map = {
'.wav': 'audio', '.mp3': 'audio',
'.csv': 'data', '.txt': 'data', '.json': 'data', '.dat': 'data',
'.rnd': 'data', '.rnh': 'data',
'.log': 'log',
'.zip': 'archive',
'.jpg': 'image', '.jpeg': 'image', '.png': 'image',
'.pdf': 'document',
}
with zipfile.ZipFile(io.BytesIO(zip_response.content)) as zf:
for zip_info in zf.filelist:
if zip_info.is_dir():
continue
file_data = zf.read(zip_info.filename)
file_path = base_dir / zip_info.filename
file_path.parent.mkdir(parents=True, exist_ok=True)
with open(file_path, 'wb') as f:
f.write(file_data)
checksum = hashlib.sha256(file_data).hexdigest()
ext = os.path.splitext(zip_info.filename)[1].lower()
data_file = DataFile(
id=str(uuid.uuid4()),
session_id=active_session.id,
file_path=str(file_path.relative_to("data")),
file_type=file_type_map.get(ext, 'data'),
file_size_bytes=len(file_data),
downloaded_at=datetime.utcnow(),
checksum=checksum,
file_metadata=json.dumps({
"source": "stop_cycle",
"remote_path": remote_path,
"unit_id": unit_id,
"folder_name": folder_name,
"relative_path": zip_info.filename,
}),
)
db.add(data_file)
files_created += 1
db.commit()
logger.info(f"Created {files_created} DataFile records for session {active_session.id} from {folder_name}")
else:
logger.warning(f"ZIP from SLMM for {folder_name} was empty or failed, skipping DataFile creation")
except Exception as e:
logger.error(f"Failed to extract ZIP and create DataFile records for {folder_name}: {e}")
# Don't fail the stop action — the device was stopped successfully
return {
"status": "stopped",
"session_id": active_session.id if active_session else None,
"cycle_response": cycle_response,
"files_created": files_created,
}
async def _execute_download(