Files
bttoxin-pipeline/backend/app/models/job.py
2025-10-13 21:05:00 +08:00

217 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""任务模型(使用 SQLModel"""
from typing import Optional, List
from datetime import datetime
from enum import Enum
from sqlmodel import SQLModel, Field, Relationship, Column
from sqlalchemy import JSON
from .base import TimestampModel, generate_uuid
class JobStatus(str, Enum):
"""任务状态"""
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"
class StepStatus(str, Enum):
"""步骤状态"""
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
SKIPPED = "skipped"
class JobBase(SQLModel):
"""Job 基础字段"""
name: str = Field(max_length=255)
description: Optional[str] = None
sequence_type: str = Field(default="nucl", max_length=20)
scaf_suffix: str = Field(default=".fna", max_length=50)
threads: int = Field(default=4, ge=1, le=32)
update_db: bool = Field(default=False)
class Job(JobBase, TimestampModel, table=True):
"""Job 数据库模型"""
__tablename__ = "jobs"
id: str = Field(
default_factory=generate_uuid,
primary_key=True,
index=True,
)
user_id: Optional[str] = Field(default=None, index=True)
status: JobStatus = Field(
default=JobStatus.PENDING,
sa_column_kwargs={"index": True},
)
input_files: List[dict] = Field(default_factory=list, sa_column=Column(JSON))
workspace_path: Optional[str] = Field(default=None, max_length=500)
result_url: Optional[str] = Field(default=None, max_length=1000)
celery_task_id: Optional[str] = Field(default=None, max_length=100, index=True)
current_step: Optional[str] = Field(default=None, max_length=100)
progress: int = Field(default=0, ge=0, le=100)
error_message: Optional[str] = None
started_at: Optional[datetime] = None
completed_at: Optional[datetime] = None
steps: List["Step"] = Relationship(
back_populates="job",
sa_relationship_kwargs={"cascade": "all, delete-orphan"},
)
logs: List["JobLog"] = Relationship(
back_populates="job",
sa_relationship_kwargs={"cascade": "all, delete-orphan"},
)
class JobCreate(JobBase):
"""创建 Job 时的请求模型"""
pass
class JobRead(JobBase):
"""读取 Job 时的响应模型"""
id: str
user_id: Optional[str]
status: JobStatus
workspace_path: Optional[str]
result_url: Optional[str]
celery_task_id: Optional[str]
current_step: Optional[str]
progress: int
error_message: Optional[str]
started_at: Optional[datetime]
completed_at: Optional[datetime]
created_at: datetime
updated_at: datetime
class JobUpdate(SQLModel):
"""更新 Job 时的请求模型"""
status: Optional[JobStatus] = None
current_step: Optional[str] = None
progress: Optional[int] = None
error_message: Optional[str] = None
started_at: Optional[datetime] = None
completed_at: Optional[datetime] = None
class StepBase(SQLModel):
"""Step 基础字段"""
step_name: str = Field(max_length=100)
step_order: int
class Step(StepBase, table=True):
"""Step 数据库模型"""
__tablename__ = "steps"
id: Optional[int] = Field(default=None, primary_key=True)
job_id: str = Field(foreign_key="jobs.id", index=True)
status: StepStatus = Field(default=StepStatus.PENDING)
celery_task_id: Optional[str] = Field(default=None, max_length=100)
log_file: Optional[str] = Field(default=None, max_length=500)
result_data: Optional[dict] = Field(default=None, sa_column=Column(JSON))
error_message: Optional[str] = None
started_at: Optional[datetime] = None
completed_at: Optional[datetime] = None
duration_seconds: Optional[int] = None
job: "Job" = Relationship(back_populates="steps")
class StepRead(StepBase):
"""读取 Step 时的响应模型"""
id: int
job_id: str
status: StepStatus
celery_task_id: Optional[str]
log_file: Optional[str]
result_data: Optional[dict]
error_message: Optional[str]
started_at: Optional[datetime]
completed_at: Optional[datetime]
duration_seconds: Optional[int]
class JobLogBase(SQLModel):
"""JobLog 基础字段"""
level: str = Field(max_length=20)
message: str
step_name: Optional[str] = Field(default=None, max_length=100)
class JobLog(JobLogBase, table=True):
"""JobLog 数据库模型"""
__tablename__ = "job_logs"
id: Optional[int] = Field(default=None, primary_key=True)
job_id: str = Field(foreign_key="jobs.id", index=True)
metadata: Optional[dict] = Field(default=None, sa_column=Column(JSON))
timestamp: datetime = Field(
default_factory=datetime.utcnow,
sa_column_kwargs={"index": True},
)
job: "Job" = Relationship(back_populates="logs")
class JobLogRead(JobLogBase):
"""读取 JobLog 时的响应模型"""
id: int
job_id: str
metadata: Optional[dict]
timestamp: datetime
"""任务模型"""
from sqlalchemy import Column, String, Integer, DateTime, JSON, Enum, Text
from sqlalchemy.sql import func
import enum
from ..database import Base
class JobStatus(str, enum.Enum):
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
class Job(Base):
__tablename__ = "jobs"
id = Column(String, primary_key=True, index=True)
celery_task_id = Column(String, nullable=True)
status = Column(Enum(JobStatus), default=JobStatus.PENDING)
input_files = Column(JSON)
sequence_type = Column(String, default="nucl")
scaf_suffix = Column(String, default=".fna")
threads = Column(Integer, default=4)
result_url = Column(String, nullable=True)
logs = Column(Text, nullable=True)
error_message = Column(Text, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
completed_at = Column(DateTime(timezone=True), nullable=True)