feat: Refactor template handling, improve scheduler functions, and add timezone utilities
- Moved Jinja2 template setup to a shared configuration file (templates_config.py) for consistent usage across routers. - Introduced timezone utilities in a new module (timezone.py) to handle UTC to local time conversions and formatting. - Updated all relevant routers to use the new shared template configuration and timezone filters. - Enhanced templates to utilize local time formatting for various datetime fields, improving user experience with timezone awareness.
This commit is contained in:
@@ -5,7 +5,6 @@ API endpoints for managing in-app alerts.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Request, Depends, HTTPException, Query
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.responses import HTMLResponse, JSONResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import Optional
|
||||
@@ -14,9 +13,9 @@ from datetime import datetime, timedelta
|
||||
from backend.database import get_db
|
||||
from backend.models import Alert, RosterUnit
|
||||
from backend.services.alert_service import get_alert_service
|
||||
from backend.templates_config import templates
|
||||
|
||||
router = APIRouter(prefix="/api/alerts", tags=["alerts"])
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
from fastapi import APIRouter, Request, Depends
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from backend.services.snapshot import emit_status_snapshot
|
||||
from backend.templates_config import templates
|
||||
|
||||
router = APIRouter()
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
||||
@router.get("/dashboard/active")
|
||||
|
||||
@@ -6,7 +6,6 @@ and unit assignments within projects.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Request, Depends, HTTPException, Query
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.responses import HTMLResponse, JSONResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import and_, or_
|
||||
@@ -24,9 +23,9 @@ from backend.models import (
|
||||
RosterUnit,
|
||||
RecordingSession,
|
||||
)
|
||||
from backend.templates_config import templates
|
||||
|
||||
router = APIRouter(prefix="/api/projects/{project_id}", tags=["project-locations"])
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
|
||||
@@ -9,17 +9,19 @@ Provides API endpoints for the Projects system:
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Request, Depends, HTTPException, Query
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func, and_
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
from collections import OrderedDict
|
||||
import uuid
|
||||
import json
|
||||
import logging
|
||||
import io
|
||||
|
||||
from backend.utils.timezone import utc_to_local, format_local_datetime
|
||||
|
||||
from backend.database import get_db
|
||||
from backend.models import (
|
||||
Project,
|
||||
@@ -31,9 +33,9 @@ from backend.models import (
|
||||
RecurringSchedule,
|
||||
RosterUnit,
|
||||
)
|
||||
from backend.templates_config import templates
|
||||
|
||||
router = APIRouter(prefix="/api/projects", tags=["projects"])
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -461,16 +463,37 @@ async def get_project_schedules(
|
||||
if status:
|
||||
query = query.filter(ScheduledAction.execution_status == status)
|
||||
|
||||
schedules = query.order_by(ScheduledAction.scheduled_time.desc()).all()
|
||||
# For pending actions, show soonest first (ascending)
|
||||
# For completed/failed, show most recent first (descending)
|
||||
if status == "pending":
|
||||
schedules = query.order_by(ScheduledAction.scheduled_time.asc()).all()
|
||||
else:
|
||||
schedules = query.order_by(ScheduledAction.scheduled_time.desc()).all()
|
||||
|
||||
# Enrich with location details
|
||||
schedules_data = []
|
||||
# Enrich with location details and group by date
|
||||
schedules_by_date = OrderedDict()
|
||||
for schedule in schedules:
|
||||
location = None
|
||||
if schedule.location_id:
|
||||
location = db.query(MonitoringLocation).filter_by(id=schedule.location_id).first()
|
||||
|
||||
schedules_data.append({
|
||||
# Get local date for grouping
|
||||
if schedule.scheduled_time:
|
||||
local_dt = utc_to_local(schedule.scheduled_time)
|
||||
date_key = local_dt.strftime("%Y-%m-%d")
|
||||
date_display = local_dt.strftime("%A, %B %d, %Y") # "Wednesday, January 22, 2026"
|
||||
else:
|
||||
date_key = "unknown"
|
||||
date_display = "Unknown Date"
|
||||
|
||||
if date_key not in schedules_by_date:
|
||||
schedules_by_date[date_key] = {
|
||||
"date_display": date_display,
|
||||
"date_key": date_key,
|
||||
"actions": [],
|
||||
}
|
||||
|
||||
schedules_by_date[date_key]["actions"].append({
|
||||
"schedule": schedule,
|
||||
"location": location,
|
||||
})
|
||||
@@ -478,7 +501,7 @@ async def get_project_schedules(
|
||||
return templates.TemplateResponse("partials/projects/schedule_list.html", {
|
||||
"request": request,
|
||||
"project_id": project_id,
|
||||
"schedules": schedules_data,
|
||||
"schedules_by_date": schedules_by_date,
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ API endpoints for managing recurring monitoring schedules.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Request, Depends, HTTPException, Query
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.responses import HTMLResponse, JSONResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import Optional
|
||||
@@ -15,9 +14,9 @@ import json
|
||||
from backend.database import get_db
|
||||
from backend.models import RecurringSchedule, MonitoringLocation, Project, RosterUnit
|
||||
from backend.services.recurring_schedule_service import get_recurring_schedule_service
|
||||
from backend.templates_config import templates
|
||||
|
||||
router = APIRouter(prefix="/api/projects/{project_id}/recurring-schedules", tags=["recurring-schedules"])
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
@@ -209,17 +208,25 @@ async def create_recurring_schedule(
|
||||
auto_increment_index=data.get("auto_increment_index", True),
|
||||
timezone=data.get("timezone", "America/New_York"),
|
||||
)
|
||||
|
||||
# Generate actions immediately so they appear right away
|
||||
generated_actions = service.generate_actions_for_schedule(schedule, horizon_days=7)
|
||||
|
||||
created_schedules.append({
|
||||
"schedule_id": schedule.id,
|
||||
"location_id": location.id,
|
||||
"location_name": location.name,
|
||||
"actions_generated": len(generated_actions),
|
||||
})
|
||||
|
||||
total_actions = sum(s.get("actions_generated", 0) for s in created_schedules)
|
||||
|
||||
return JSONResponse({
|
||||
"success": True,
|
||||
"schedules": created_schedules,
|
||||
"count": len(created_schedules),
|
||||
"message": f"Created {len(created_schedules)} recurring schedule(s)",
|
||||
"actions_generated": total_actions,
|
||||
"message": f"Created {len(created_schedules)} recurring schedule(s) with {total_actions} upcoming actions",
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ Handles scheduled actions for automated recording control.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Request, Depends, HTTPException, Query
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.responses import HTMLResponse, JSONResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import and_, or_
|
||||
@@ -23,9 +22,9 @@ from backend.models import (
|
||||
RosterUnit,
|
||||
)
|
||||
from backend.services.scheduler import get_scheduler
|
||||
from backend.templates_config import templates
|
||||
|
||||
router = APIRouter(prefix="/api/projects/{project_id}/scheduler", tags=["scheduler"])
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
|
||||
@@ -5,13 +5,12 @@ Provides endpoints for the seismograph-specific dashboard
|
||||
|
||||
from fastapi import APIRouter, Request, Depends, Query
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from sqlalchemy.orm import Session
|
||||
from backend.database import get_db
|
||||
from backend.models import RosterUnit
|
||||
from backend.templates_config import templates
|
||||
|
||||
router = APIRouter(prefix="/api/seismo-dashboard", tags=["seismo-dashboard"])
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
||||
@router.get("/stats", response_class=HTMLResponse)
|
||||
|
||||
@@ -5,7 +5,6 @@ Provides API endpoints for the Sound Level Meters dashboard page.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Request, Depends, Query
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.responses import HTMLResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func
|
||||
@@ -18,11 +17,11 @@ import os
|
||||
from backend.database import get_db
|
||||
from backend.models import RosterUnit
|
||||
from backend.routers.roster_edit import sync_slm_to_slmm_cache
|
||||
from backend.templates_config import templates
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/api/slm-dashboard", tags=["slm-dashboard"])
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
# SLMM backend URL - configurable via environment variable
|
||||
SLMM_BASE_URL = os.getenv("SLMM_BASE_URL", "http://localhost:8100")
|
||||
|
||||
@@ -6,7 +6,6 @@ Provides endpoints for SLM dashboard cards, detail pages, and real-time data.
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from sqlalchemy.orm import Session
|
||||
from datetime import datetime
|
||||
import httpx
|
||||
@@ -15,11 +14,11 @@ import os
|
||||
|
||||
from backend.database import get_db
|
||||
from backend.models import RosterUnit
|
||||
from backend.templates_config import templates
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/slm", tags=["slm-ui"])
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
SLMM_BASE_URL = os.getenv("SLMM_BASE_URL", "http://172.19.0.1:8100")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user