added activity view

This commit is contained in:
2026-01-09 09:59:36 -08:00
parent c45e41b6a9
commit 55e37fbca8
168 changed files with 8799 additions and 2426 deletions

View File

@@ -0,0 +1,131 @@
from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel
from sqlalchemy.orm import Session
from typing import List, Optional
from datetime import datetime, timedelta
import json
import logging
from ..models.scheduled_job import ScheduledJob
from ..services.postgresql_manager import PostgreSQLManager
from ..utils.config import config
from ..services.scheduler import scheduler
router = APIRouter()
logger = logging.getLogger(__name__)
def get_db():
db_manager = PostgreSQLManager(config.DATABASE_URL)
with db_manager.get_db_session() as session:
yield session
class ScheduledJobResponse(BaseModel):
id: int
job_type: str
name: str
interval_minutes: int
enabled: bool
last_run: Optional[datetime]
next_run: Optional[datetime]
params: Optional[str]
class Config:
from_attributes = True
class JobUpdateRequest(BaseModel):
interval_minutes: Optional[int] = None
enabled: Optional[bool] = None
params: Optional[dict] = None
@router.get("/scheduling/jobs", response_model=List[ScheduledJobResponse])
def list_scheduled_jobs(db: Session = Depends(get_db)):
"""List all scheduled jobs."""
jobs = db.query(ScheduledJob).order_by(ScheduledJob.id).all()
return jobs
@router.put("/scheduling/jobs/{job_id}", response_model=ScheduledJobResponse)
def update_scheduled_job(job_id: int, request: JobUpdateRequest, db: Session = Depends(get_db)):
"""Update a scheduled job's interval or enabled status."""
job = db.query(ScheduledJob).filter(ScheduledJob.id == job_id).first()
if not job:
raise HTTPException(status_code=404, detail="Job not found")
if request.interval_minutes is not None:
if request.interval_minutes < 1:
raise HTTPException(status_code=400, detail="Interval must be at least 1 minute")
job.interval_minutes = request.interval_minutes
# If enabled, update next_run based on new interval if it's far in future?
# Actually, standard behavior: next_run should be recalculated from last_run + new interval
# OR just leave it. If we shorten it, we might want it to run sooner.
# Let's recalculate next_run if it exists.
if job.last_run:
job.next_run = job.last_run + timedelta(minutes=job.interval_minutes)
else:
# If never run, next_run should be Now if enabled?
# Or keep existing next_run?
# If next_run is null and enabled, scheduler picks it up immediately.
pass
if request.enabled is not None:
job.enabled = request.enabled
if job.enabled and job.next_run is None:
# If re-enabling and no next run, set to now
job.next_run = datetime.now()
if request.params is not None:
job.params = json.dumps(request.params)
db.commit()
db.refresh(job)
return job
class JobCreateRequest(BaseModel):
job_type: str
name: str
interval_minutes: int
params: Optional[dict] = {}
enabled: Optional[bool] = True
@router.post("/scheduling/jobs", response_model=ScheduledJobResponse)
def create_scheduled_job(request: JobCreateRequest, db: Session = Depends(get_db)):
"""Create a new scheduled job."""
# Validate job_type
from ..services.scheduler import scheduler
if request.job_type not in scheduler.TASK_MAP:
raise HTTPException(status_code=400, detail=f"Invalid job_type. Must be one of: {list(scheduler.TASK_MAP.keys())}")
new_job = ScheduledJob(
job_type=request.job_type,
name=request.name,
interval_minutes=request.interval_minutes,
params=json.dumps(request.params) if request.params else "{}",
enabled=request.enabled,
next_run=datetime.now() if request.enabled else None
)
try:
db.add(new_job)
db.commit()
db.refresh(new_job)
return new_job
except Exception as e:
db.rollback()
logger.error(f"Failed to create job: {e}")
# Check for unique constraint on job_type if we enforced it?
# The model has job_type unique=True. This might be a problem if we want multiple of same type?
# User wants "new scheduled tasks" with "variables" -> implies multiple of same type (e.g. sync fitbit 10 days vs 30 days).
# We need to remove unique=True from ScheduledJob model if it exists!
raise HTTPException(status_code=400, detail=f"Failed to create job: {str(e)}")
@router.delete("/scheduling/jobs/{job_id}", status_code=204)
def delete_scheduled_job(job_id: int, db: Session = Depends(get_db)):
"""Delete a scheduled job."""
job = db.query(ScheduledJob).filter(ScheduledJob.id == job_id).first()
if not job:
raise HTTPException(status_code=404, detail="Job not found")
db.delete(job)
db.commit()
return None