feat(reports): per-NRL Data Files tab reaches parity with the project-wide tab

The per-NRL Data Files tab now reuses the same FTP browser + unified-files partials as the project-wide tab, scoped to the one NRL: ftp-browser and files-unified take an optional location_id. nrl_detail.html drops the flat file_list view for 'Download Files from SLMs' (Browse Files -> Download & Save) plus the grouped 'Project Files' view (edit times / download-all / delete), keeping the NRL upload and adding a refresh button.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 18:46:59 +00:00
parent 7716a4b51d
commit aa21c81c2e
2 changed files with 42 additions and 12 deletions
+20 -7
View File
@@ -1591,24 +1591,32 @@ async def get_sessions_calendar(
async def get_ftp_browser(
project_id: str,
request: Request,
location_id: Optional[str] = None,
db: Session = Depends(get_db),
):
"""
Get FTP browser interface for downloading files from assigned SLMs.
Returns HTML partial with FTP browser. Sound Monitoring projects only.
When `location_id` is given, scope to just the unit(s) assigned to that NRL
(used by the per-NRL Data Files tab, which mirrors the project-wide tab).
"""
from backend.models import DataFile
project = db.query(Project).filter_by(id=project_id).first()
_require_module(project, "sound_monitoring", db)
# Get all assignments for this project (active = assigned_until IS NULL)
assignments = db.query(UnitAssignment).filter(
# Active assignments for this project (active = assigned_until IS NULL),
# optionally scoped to a single NRL/location.
q = db.query(UnitAssignment).filter(
and_(
UnitAssignment.project_id == project_id,
UnitAssignment.assigned_until == None,
)
).all()
)
if location_id:
q = q.filter(UnitAssignment.location_id == location_id)
assignments = q.all()
# Enrich with unit and location details
units_data = []
@@ -1882,21 +1890,26 @@ async def ftp_download_folder_to_server(
async def get_unified_files(
project_id: str,
request: Request,
location_id: Optional[str] = None,
db: Session = Depends(get_db),
):
"""
Get unified view of all files in this project.
Groups files by recording session with full metadata.
Returns HTML partial with hierarchical file listing.
When `location_id` is given, scope to a single NRL/location (used by the
per-NRL Data Files tab so it mirrors the project-wide tab).
"""
from backend.models import DataFile
from pathlib import Path
import json
# Get all sessions for this project
sessions = db.query(MonitoringSession).filter_by(
project_id=project_id
).order_by(MonitoringSession.started_at.desc()).all()
# Sessions for this project (optionally scoped to one NRL/location)
q = db.query(MonitoringSession).filter_by(project_id=project_id)
if location_id:
q = q.filter(MonitoringSession.location_id == location_id)
sessions = q.order_by(MonitoringSession.started_at.desc()).all()
sessions_data = []
for session in sessions: