Files
backup-tool/backend/app/models.py
T
alex 955ed9db49 fix: jobs router session handling, schedule creation, and tests
- Use new session in background task for thread safety
- Fix schedule creation to associate with job
- Fix schedule endpoint return type
- Update tests to use AsyncClient
- Add missing tests for list, get, delete, schedule
- Add updated_at field to Schedule model
2026-05-11 21:20:31 +02:00

138 lines
4.7 KiB
Python

from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text, ForeignKey, JSON
from sqlalchemy.orm import relationship
from datetime import datetime, timezone
from .database import Base
class Source(Base):
__tablename__ = "sources"
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
type = Column(String, nullable=False) # local, ssh, database
config = Column(JSON, default=lambda: {})
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
updated_at = Column(
DateTime,
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc),
)
jobs = relationship("Job", back_populates="source", cascade="all, delete-orphan")
def __repr__(self):
return f"<Source(id={self.id}, name='{self.name}', type='{self.type}')>"
class Job(Base):
__tablename__ = "jobs"
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
source_id = Column(Integer, ForeignKey("sources.id"), nullable=False, index=True)
strategy = Column(String, nullable=False, default="full") # full, incremental
destination_path = Column(String, nullable=False)
exclude_patterns = Column(JSON, default=lambda: [])
enabled = Column(Boolean, default=True)
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
updated_at = Column(
DateTime,
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc),
)
source = relationship("Source", back_populates="jobs")
schedule = relationship(
"Schedule", back_populates="job", uselist=False, cascade="all, delete-orphan"
)
executions = relationship(
"JobExecution", back_populates="job", cascade="all, delete-orphan"
)
def __repr__(self):
return f"<Job(id={self.id}, name='{self.name}', source_id={self.source_id})>"
class Schedule(Base):
__tablename__ = "schedules"
id = Column(Integer, primary_key=True)
job_id = Column(
Integer, ForeignKey("jobs.id"), unique=True, nullable=False, index=True
)
cron_expression = Column(String, nullable=False)
enabled = Column(Boolean, default=True)
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
updated_at = Column(
DateTime,
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc),
)
job = relationship("Job", back_populates="schedule")
def __repr__(self):
return f"<Schedule(id={self.id}, job_id={self.job_id}, cron='{self.cron_expression}')>"
class JobExecution(Base):
__tablename__ = "job_executions"
id = Column(Integer, primary_key=True)
job_id = Column(Integer, ForeignKey("jobs.id"), nullable=False, index=True)
status = Column(
String, nullable=False, default="pending"
) # pending, running, success, failed, cancelled
started_at = Column(DateTime, nullable=True)
completed_at = Column(DateTime, nullable=True)
bytes_processed = Column(Integer, default=0)
bytes_backed_up = Column(Integer, default=0)
error_message = Column(Text, nullable=True)
triggered_by = Column(String, nullable=False) # manual, schedule
job = relationship("Job", back_populates="executions")
backups = relationship(
"Backup", back_populates="execution", cascade="all, delete-orphan"
)
def __repr__(self):
return f"<JobExecution(id={self.id}, job_id={self.job_id}, status='{self.status}')>"
class Backup(Base):
__tablename__ = "backups"
id = Column(Integer, primary_key=True)
execution_id = Column(
Integer, ForeignKey("job_executions.id"), nullable=False, index=True
)
storage_path = Column(String, nullable=False)
size_bytes = Column(Integer, default=0)
checksum = Column(String, nullable=True)
type = Column(String, nullable=False) # full, incremental
parent_backup_id = Column(
Integer, ForeignKey("backups.id"), nullable=True, index=True
)
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
execution = relationship("JobExecution", back_populates="backups")
parent_backup = relationship("Backup", remote_side=[id])
def __repr__(self):
return f"<Backup(id={self.id}, execution_id={self.execution_id}, type='{self.type}')>"
class Setting(Base):
__tablename__ = "settings"
key = Column(String, primary_key=True)
value = Column(Text, nullable=True)
updated_at = Column(
DateTime,
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc),
)
def __repr__(self):
return f"<Setting(key='{self.key}', value='{self.value}')>"