Add schedule and unit list templates for project management
- Created `schedule_list.html` to display scheduled actions with execution status, location, and timestamps. - Implemented buttons for executing and canceling schedules, along with a details view placeholder. - Created `unit_list.html` to show assigned units with their status, location, model, and session/file counts. - Added conditional rendering for active sessions and links to view unit and location details.
This commit is contained in:
@@ -341,6 +341,230 @@ async def get_project_dashboard(
|
||||
})
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Project Types
|
||||
# ============================================================================
|
||||
|
||||
@router.get("/{project_id}/header", response_class=JSONResponse)
|
||||
async def get_project_header(project_id: str, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Get project header information for dynamic display.
|
||||
Returns JSON with project name, status, and type.
|
||||
"""
|
||||
project = db.query(Project).filter_by(id=project_id).first()
|
||||
if not project:
|
||||
raise HTTPException(status_code=404, detail="Project not found")
|
||||
|
||||
project_type = db.query(ProjectType).filter_by(id=project.project_type_id).first()
|
||||
|
||||
return JSONResponse({
|
||||
"id": project.id,
|
||||
"name": project.name,
|
||||
"status": project.status,
|
||||
"project_type_id": project.project_type_id,
|
||||
"project_type_name": project_type.name if project_type else None,
|
||||
})
|
||||
|
||||
|
||||
@router.get("/{project_id}/units", response_class=HTMLResponse)
|
||||
async def get_project_units(
|
||||
project_id: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Get all units assigned to this project's locations.
|
||||
Returns HTML partial with unit list.
|
||||
"""
|
||||
from backend.models import DataFile
|
||||
|
||||
# Get all assignments for this project
|
||||
assignments = db.query(UnitAssignment).filter(
|
||||
and_(
|
||||
UnitAssignment.project_id == project_id,
|
||||
UnitAssignment.status == "active",
|
||||
)
|
||||
).all()
|
||||
|
||||
# Enrich with unit and location details
|
||||
units_data = []
|
||||
for assignment in assignments:
|
||||
unit = db.query(RosterUnit).filter_by(id=assignment.unit_id).first()
|
||||
location = db.query(MonitoringLocation).filter_by(id=assignment.location_id).first()
|
||||
|
||||
# Count sessions for this assignment
|
||||
session_count = db.query(func.count(RecordingSession.id)).filter_by(
|
||||
location_id=assignment.location_id,
|
||||
unit_id=assignment.unit_id,
|
||||
).scalar()
|
||||
|
||||
# Count files from sessions
|
||||
file_count = db.query(func.count(DataFile.id)).join(
|
||||
RecordingSession,
|
||||
DataFile.session_id == RecordingSession.id
|
||||
).filter(
|
||||
RecordingSession.location_id == assignment.location_id,
|
||||
RecordingSession.unit_id == assignment.unit_id,
|
||||
).scalar()
|
||||
|
||||
# Check if currently recording
|
||||
active_session = db.query(RecordingSession).filter(
|
||||
and_(
|
||||
RecordingSession.location_id == assignment.location_id,
|
||||
RecordingSession.unit_id == assignment.unit_id,
|
||||
RecordingSession.status == "recording",
|
||||
)
|
||||
).first()
|
||||
|
||||
units_data.append({
|
||||
"assignment": assignment,
|
||||
"unit": unit,
|
||||
"location": location,
|
||||
"session_count": session_count,
|
||||
"file_count": file_count,
|
||||
"active_session": active_session,
|
||||
})
|
||||
|
||||
# Get project type for label context
|
||||
project = db.query(Project).filter_by(id=project_id).first()
|
||||
project_type = db.query(ProjectType).filter_by(id=project.project_type_id).first() if project else None
|
||||
|
||||
return templates.TemplateResponse("partials/projects/unit_list.html", {
|
||||
"request": request,
|
||||
"project_id": project_id,
|
||||
"units": units_data,
|
||||
"project_type": project_type,
|
||||
})
|
||||
|
||||
|
||||
@router.get("/{project_id}/schedules", response_class=HTMLResponse)
|
||||
async def get_project_schedules(
|
||||
project_id: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
status: Optional[str] = Query(None),
|
||||
):
|
||||
"""
|
||||
Get scheduled actions for this project.
|
||||
Returns HTML partial with schedule list.
|
||||
Optional status filter: pending, completed, failed, cancelled
|
||||
"""
|
||||
query = db.query(ScheduledAction).filter_by(project_id=project_id)
|
||||
|
||||
# Filter by status if provided
|
||||
if status:
|
||||
query = query.filter(ScheduledAction.execution_status == status)
|
||||
|
||||
schedules = query.order_by(ScheduledAction.scheduled_time.desc()).all()
|
||||
|
||||
# Enrich with location details
|
||||
schedules_data = []
|
||||
for schedule in schedules:
|
||||
location = None
|
||||
if schedule.location_id:
|
||||
location = db.query(MonitoringLocation).filter_by(id=schedule.location_id).first()
|
||||
|
||||
schedules_data.append({
|
||||
"schedule": schedule,
|
||||
"location": location,
|
||||
})
|
||||
|
||||
return templates.TemplateResponse("partials/projects/schedule_list.html", {
|
||||
"request": request,
|
||||
"project_id": project_id,
|
||||
"schedules": schedules_data,
|
||||
})
|
||||
|
||||
|
||||
@router.get("/{project_id}/sessions", response_class=HTMLResponse)
|
||||
async def get_project_sessions(
|
||||
project_id: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
status: Optional[str] = Query(None),
|
||||
):
|
||||
"""
|
||||
Get all recording sessions for this project.
|
||||
Returns HTML partial with session list.
|
||||
Optional status filter: recording, completed, paused, failed
|
||||
"""
|
||||
query = db.query(RecordingSession).filter_by(project_id=project_id)
|
||||
|
||||
# Filter by status if provided
|
||||
if status:
|
||||
query = query.filter(RecordingSession.status == status)
|
||||
|
||||
sessions = query.order_by(RecordingSession.started_at.desc()).all()
|
||||
|
||||
# Enrich with unit and location details
|
||||
sessions_data = []
|
||||
for session in sessions:
|
||||
unit = None
|
||||
location = None
|
||||
|
||||
if session.unit_id:
|
||||
unit = db.query(RosterUnit).filter_by(id=session.unit_id).first()
|
||||
if session.location_id:
|
||||
location = db.query(MonitoringLocation).filter_by(id=session.location_id).first()
|
||||
|
||||
sessions_data.append({
|
||||
"session": session,
|
||||
"unit": unit,
|
||||
"location": location,
|
||||
})
|
||||
|
||||
return templates.TemplateResponse("partials/projects/session_list.html", {
|
||||
"request": request,
|
||||
"project_id": project_id,
|
||||
"sessions": sessions_data,
|
||||
})
|
||||
|
||||
|
||||
@router.get("/{project_id}/files", response_class=HTMLResponse)
|
||||
async def get_project_files(
|
||||
project_id: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
file_type: Optional[str] = Query(None),
|
||||
):
|
||||
"""
|
||||
Get all data files from all sessions in this project.
|
||||
Returns HTML partial with file list.
|
||||
Optional file_type filter: audio, data, log, etc.
|
||||
"""
|
||||
from backend.models import DataFile
|
||||
|
||||
# Join through RecordingSession to get project files
|
||||
query = db.query(DataFile).join(
|
||||
RecordingSession,
|
||||
DataFile.session_id == RecordingSession.id
|
||||
).filter(RecordingSession.project_id == project_id)
|
||||
|
||||
# Filter by file type if provided
|
||||
if file_type:
|
||||
query = query.filter(DataFile.file_type == file_type)
|
||||
|
||||
files = query.order_by(DataFile.created_at.desc()).all()
|
||||
|
||||
# Enrich with session details
|
||||
files_data = []
|
||||
for file in files:
|
||||
session = None
|
||||
if file.session_id:
|
||||
session = db.query(RecordingSession).filter_by(id=file.session_id).first()
|
||||
|
||||
files_data.append({
|
||||
"file": file,
|
||||
"session": session,
|
||||
})
|
||||
|
||||
return templates.TemplateResponse("partials/projects/file_list.html", {
|
||||
"request": request,
|
||||
"project_id": project_id,
|
||||
"files": files_data,
|
||||
})
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Project Types
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user