diff --git a/backend/routers/dashboard.py b/backend/routers/dashboard.py index c9e61bb..4d36f52 100644 --- a/backend/routers/dashboard.py +++ b/backend/routers/dashboard.py @@ -1,5 +1,6 @@ from fastapi import APIRouter, Request, Depends from sqlalchemy.orm import Session +from sqlalchemy import and_ from datetime import datetime, timedelta from backend.database import get_db @@ -48,10 +49,18 @@ def dashboard_todays_actions(request: Request, db: Session = Depends(get_db)): today_start_utc = today_start_local.astimezone(ZoneInfo("UTC")).replace(tzinfo=None) today_end_utc = today_end_local.astimezone(ZoneInfo("UTC")).replace(tzinfo=None) + # Exclude actions from paused/removed projects + paused_project_ids = [ + p.id for p in db.query(Project.id).filter( + Project.status.in_(["on_hold", "archived", "deleted"]) + ).all() + ] + # Query today's actions actions = db.query(ScheduledAction).filter( ScheduledAction.scheduled_time >= today_start_utc, ScheduledAction.scheduled_time < today_end_utc, + ScheduledAction.project_id.notin_(paused_project_ids), ).order_by(ScheduledAction.scheduled_time.asc()).all() # Enrich with location/project info and parse results diff --git a/backend/routers/projects.py b/backend/routers/projects.py index 4beec68..1316a79 100644 --- a/backend/routers/projects.py +++ b/backend/routers/projects.py @@ -342,6 +342,14 @@ async def update_project( project.description = data["description"] if "status" in data: project.status = data["status"] + # Cancel pending scheduled actions when archiving + if data["status"] == "archived": + db.query(ScheduledAction).filter( + and_( + ScheduledAction.project_id == project_id, + ScheduledAction.execution_status == "pending", + ) + ).update({"execution_status": "cancelled"}) if "client_name" in data: project.client_name = data["client_name"] if "site_address" in data: @@ -374,6 +382,14 @@ async def delete_project(project_id: str, db: Session = Depends(get_db)): project.deleted_at = datetime.utcnow() project.updated_at = datetime.utcnow() + # Cancel all pending scheduled actions + db.query(ScheduledAction).filter( + and_( + ScheduledAction.project_id == project_id, + ScheduledAction.execution_status == "pending", + ) + ).update({"execution_status": "cancelled"}) + db.commit() return {"success": True, "message": "Project deleted. Data will be permanently removed after 60 days."} @@ -414,6 +430,15 @@ async def hold_project(project_id: str, db: Session = Depends(get_db)): project.status = "on_hold" project.updated_at = datetime.utcnow() + + # Cancel pending scheduled actions so they don't appear in dashboards or fire + db.query(ScheduledAction).filter( + and_( + ScheduledAction.project_id == project_id, + ScheduledAction.execution_status == "pending", + ) + ).update({"execution_status": "cancelled"}) + db.commit() return {"success": True, "message": "Project put on hold."} @@ -672,10 +697,14 @@ async def get_project_schedules( "result": result_data, }) + project = db.query(Project).filter_by(id=project_id).first() + project_status = project.status if project else "active" + return templates.TemplateResponse("partials/projects/schedule_list.html", { "request": request, "project_id": project_id, "schedules_by_date": schedules_by_date, + "project_status": project_status, }) diff --git a/backend/routers/recurring_schedules.py b/backend/routers/recurring_schedules.py index 9a0289b..ee019a0 100644 --- a/backend/routers/recurring_schedules.py +++ b/backend/routers/recurring_schedules.py @@ -497,6 +497,9 @@ async def get_schedule_list_partial( """ Return HTML partial for schedule list. """ + project = db.query(Project).filter_by(id=project_id).first() + project_status = project.status if project else "active" + schedules = db.query(RecurringSchedule).filter_by( project_id=project_id ).order_by(RecurringSchedule.created_at.desc()).all() @@ -515,4 +518,5 @@ async def get_schedule_list_partial( "request": request, "project_id": project_id, "schedules": schedule_data, + "project_status": project_status, }) diff --git a/backend/services/recurring_schedule_service.py b/backend/services/recurring_schedule_service.py index bce6f7b..8ef3f17 100644 --- a/backend/services/recurring_schedule_service.py +++ b/backend/services/recurring_schedule_service.py @@ -15,7 +15,7 @@ from zoneinfo import ZoneInfo from sqlalchemy.orm import Session from sqlalchemy import and_ -from backend.models import RecurringSchedule, ScheduledAction, MonitoringLocation, UnitAssignment +from backend.models import RecurringSchedule, ScheduledAction, MonitoringLocation, UnitAssignment, Project logger = logging.getLogger(__name__) @@ -594,8 +594,16 @@ class RecurringScheduleService: return self.db.query(RecurringSchedule).filter_by(project_id=project_id).all() def get_enabled_schedules(self) -> List[RecurringSchedule]: - """Get all enabled recurring schedules.""" - return self.db.query(RecurringSchedule).filter_by(enabled=True).all() + """Get all enabled recurring schedules for projects that are not on hold or deleted.""" + active_project_ids = [ + p.id for p in self.db.query(Project.id).filter( + Project.status.notin_(["on_hold", "archived", "deleted"]) + ).all() + ] + return self.db.query(RecurringSchedule).filter( + RecurringSchedule.enabled == True, + RecurringSchedule.project_id.in_(active_project_ids), + ).all() def get_recurring_schedule_service(db: Session) -> RecurringScheduleService: diff --git a/backend/services/scheduler.py b/backend/services/scheduler.py index 7b9da92..8e40e67 100644 --- a/backend/services/scheduler.py +++ b/backend/services/scheduler.py @@ -107,10 +107,19 @@ class SchedulerService: try: # Find pending actions that are due now = datetime.utcnow() + + # Only execute actions for active/completed projects (not on_hold, archived, or deleted) + active_project_ids = [ + p.id for p in db.query(Project.id).filter( + Project.status.notin_(["on_hold", "archived", "deleted"]) + ).all() + ] + pending_actions = db.query(ScheduledAction).filter( and_( ScheduledAction.execution_status == "pending", ScheduledAction.scheduled_time <= now, + ScheduledAction.project_id.in_(active_project_ids), ) ).order_by(ScheduledAction.scheduled_time).all() diff --git a/templates/partials/projects/recurring_schedule_list.html b/templates/partials/projects/recurring_schedule_list.html index c5279d8..6084598 100644 --- a/templates/partials/projects/recurring_schedule_list.html +++ b/templates/partials/projects/recurring_schedule_list.html @@ -5,7 +5,7 @@ {% if schedules %} {% for item in schedules %}