added activity view
This commit is contained in:
131
FitnessSync/backend/src/api/scheduling.py
Normal file
131
FitnessSync/backend/src/api/scheduling.py
Normal 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
|
||||
Reference in New Issue
Block a user