[fix/feat]:[FL-81][ATP模型管理改造为资产发布制]

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
chengkai3
2026-06-11 22:39:48 +08:00
parent de22a76f70
commit fac37ddb8d
14 changed files with 3543 additions and 3 deletions
+2
View File
@@ -2,6 +2,7 @@ from fastapi import APIRouter
from .v1.admin import router as admin_router
from .v1.admin_files import router as admin_files_router
from .v1.atp_assets import router as atp_assets_router
from .v1.atp_models import router as atp_models_router
from .v1.auth import router as auth_router
from .v1.elevation import router as elevation_router
@@ -24,6 +25,7 @@ v1_router.include_router(auth_router)
v1_router.include_router(users_router)
v1_router.include_router(admin_router)
v1_router.include_router(admin_files_router)
v1_router.include_router(atp_assets_router)
v1_router.include_router(atp_models_router)
v1_router.include_router(task_monitor_router)
v1_router.include_router(scheduled_tasks_router)
+241
View File
@@ -0,0 +1,241 @@
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.orm import Session
from ...core.database import get_db
from ...core.dependencies import CurrentUser, require_any_permission, require_permission
from ...schemas.atp_asset import (
AtpAssetCreateRequest,
AtpAssetDetail,
AtpAssetFileListResponse,
AtpAssetListResponse,
AtpAssetReleaseCreateRequest,
AtpAssetReleaseDetail,
AtpAssetReleaseListResponse,
AtpAssetRunDetail,
AtpAssetRunListResponse,
AtpAssetRunRequest,
AtpAssetUpdateRequest,
)
from ...schemas.atp_model import AtpEngineStatusResponse
from ...services.atp_asset_service import (
activate_release,
create_asset,
create_release,
delete_asset,
get_asset_by_id,
get_release_by_id,
get_run_detail,
list_assets,
list_release_files,
list_releases,
list_runs,
run_release,
serialize_asset,
serialize_release_detail,
update_asset,
update_release,
)
from ...services.atp_model_service import get_engine_status
router = APIRouter(prefix="/atp", tags=["atp-assets"])
@router.get("/engine/status", response_model=AtpEngineStatusResponse)
def get_atp_engine_status_endpoint(
_: CurrentUser = Depends(require_any_permission("atp.read", "atp.run", "atp.manage")),
) -> AtpEngineStatusResponse:
return get_engine_status()
@router.get("/assets", response_model=AtpAssetListResponse)
def get_atp_asset_list(
keyword: str | None = Query(default=None),
status_filter: str | None = Query(default=None, alias="status"),
voltage_level: str | None = Query(default=None),
tower_type: str | None = Query(default=None),
scene_type: str | None = Query(default=None),
_: CurrentUser = Depends(require_any_permission("atp.read", "atp.run", "atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetListResponse:
return list_assets(
db,
keyword=keyword,
status_filter=status_filter,
voltage_level=voltage_level,
tower_type=tower_type,
scene_type=scene_type,
)
@router.post("/assets", response_model=AtpAssetDetail)
def create_atp_asset_endpoint(
payload: AtpAssetCreateRequest,
current_user: CurrentUser = Depends(require_permission("atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetDetail:
created = create_asset(db, payload, actor_user_id=current_user.user.id)
if not created:
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Asset code already exists")
return AtpAssetDetail(**created.model_dump())
@router.get("/assets/{asset_id}", response_model=AtpAssetDetail)
def get_atp_asset_detail(
asset_id: str,
_: CurrentUser = Depends(require_any_permission("atp.read", "atp.run", "atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetDetail:
item = get_asset_by_id(db, asset_id)
if not item:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Asset not found")
active_release = next((release for release in item.releases if release.is_active), None)
detail = serialize_asset(
item,
release_count=len(item.releases),
run_count=len(item.runs),
last_run_status=item.runs[0].status if item.runs else None,
last_run_date=item.runs[0].create_date if item.runs else None,
active_release=active_release,
)
return AtpAssetDetail(**detail.model_dump())
@router.patch("/assets/{asset_id}", response_model=AtpAssetDetail)
def update_atp_asset_endpoint(
asset_id: str,
payload: AtpAssetUpdateRequest,
current_user: CurrentUser = Depends(require_permission("atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetDetail:
updated = update_asset(db, asset_id, payload, actor_user_id=current_user.user.id)
if not updated:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Asset not found")
return AtpAssetDetail(**updated.model_dump())
@router.delete("/assets/{asset_id}")
def delete_atp_asset_endpoint(
asset_id: str,
_: CurrentUser = Depends(require_permission("atp.manage")),
db: Session = Depends(get_db),
) -> dict[str, bool]:
deleted = delete_asset(db, asset_id)
if not deleted:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Asset not found")
return {"success": True}
@router.get("/assets/{asset_id}/releases", response_model=AtpAssetReleaseListResponse)
def get_atp_asset_releases(
asset_id: str,
limit: int = Query(default=200, ge=1, le=500),
offset: int = Query(default=0, ge=0),
_: CurrentUser = Depends(require_any_permission("atp.read", "atp.run", "atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetReleaseListResponse:
if not get_asset_by_id(db, asset_id):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Asset not found")
return list_releases(db, asset_id=asset_id, limit=limit, offset=offset)
@router.post("/assets/{asset_id}/releases", response_model=AtpAssetReleaseDetail)
def create_atp_asset_release_endpoint(
asset_id: str,
payload: AtpAssetReleaseCreateRequest,
current_user: CurrentUser = Depends(require_permission("atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetReleaseDetail:
return create_release(db, asset_id=asset_id, payload=payload, actor_user_id=current_user.user.id)
@router.get("/releases", response_model=AtpAssetReleaseListResponse)
def get_atp_release_list(
active_only: bool = Query(default=False),
status_filter: str | None = Query(default=None, alias="status"),
limit: int = Query(default=200, ge=1, le=500),
offset: int = Query(default=0, ge=0),
_: CurrentUser = Depends(require_any_permission("atp.read", "atp.run", "atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetReleaseListResponse:
return list_releases(
db,
active_only=active_only,
status_filter=status_filter,
limit=limit,
offset=offset,
)
@router.get("/releases/{release_id}", response_model=AtpAssetReleaseDetail)
def get_atp_release_detail(
release_id: str,
_: CurrentUser = Depends(require_any_permission("atp.read", "atp.run", "atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetReleaseDetail:
item = get_release_by_id(db, release_id)
if not item:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Release not found")
return serialize_release_detail(item)
@router.patch("/releases/{release_id}", response_model=AtpAssetReleaseDetail)
def update_atp_release_endpoint(
release_id: str,
payload: AtpAssetReleaseUpdateRequest,
current_user: CurrentUser = Depends(require_permission("atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetReleaseDetail:
return update_release(db, release_id=release_id, payload=payload, actor_user_id=current_user.user.id)
@router.post("/releases/{release_id}/activate", response_model=AtpAssetDetail)
def activate_atp_release_endpoint(
release_id: str,
current_user: CurrentUser = Depends(require_permission("atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetDetail:
detail = activate_release(db, release_id=release_id, actor_user_id=current_user.user.id)
return AtpAssetDetail(**detail.model_dump())
@router.get("/releases/{release_id}/files", response_model=AtpAssetFileListResponse)
def get_atp_release_files(
release_id: str,
_: CurrentUser = Depends(require_any_permission("atp.read", "atp.run", "atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetFileListResponse:
return list_release_files(db, release_id=release_id)
@router.get("/releases/{release_id}/runs", response_model=AtpAssetRunListResponse)
def get_atp_release_runs(
release_id: str,
limit: int = Query(default=100, ge=1, le=500),
offset: int = Query(default=0, ge=0),
_: CurrentUser = Depends(require_any_permission("atp.read", "atp.run", "atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetRunListResponse:
if not get_release_by_id(db, release_id):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Release not found")
return list_runs(db, release_id=release_id, limit=limit, offset=offset)
@router.post("/releases/{release_id}/runs", response_model=AtpAssetRunDetail)
def run_atp_release_endpoint(
release_id: str,
payload: AtpAssetRunRequest,
current_user: CurrentUser = Depends(require_any_permission("atp.run", "atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetRunDetail:
return run_release(db, release_id=release_id, payload=payload, actor_user_id=current_user.user.id)
@router.get("/runs/{run_id}", response_model=AtpAssetRunDetail)
def get_atp_run_detail(
run_id: str,
_: CurrentUser = Depends(require_any_permission("atp.read", "atp.run", "atp.manage")),
db: Session = Depends(get_db),
) -> AtpAssetRunDetail:
return get_run_detail(db, run_id=run_id)
+1
View File
@@ -11,6 +11,7 @@ celery_app = Celery(
broker=settings.resolved_celery_broker_url,
backend=settings.resolved_celery_result_backend,
include=[
"app.tasks.atp_asset_tasks",
"app.tasks.atp_model_tasks",
"app.tasks.elevation_tasks",
"app.tasks.fl_analysis_tasks",
+1
View File
@@ -490,6 +490,7 @@ def get_db() -> Generator[Session, None, None]:
def init_db() -> None:
# Import models so metadata includes every table before create_all.
from ..models import (
atp_asset,
atp_model,
audit_log,
auth_session,
+2 -1
View File
@@ -4,9 +4,10 @@ Import all model modules during package initialization so SQLAlchemy can
resolve string-based relationships regardless of route/service import order.
"""
from . import atp_model, audit_log, auth_session, elevation, file_storage, fl_analysis, lightning_event, lightning_sample, line, line_tower, menu, object_group, rbac, scheduled_task, system_param, tower_model, tower_profile, user, wine, worker_registry
from . import atp_asset, atp_model, audit_log, auth_session, elevation, file_storage, fl_analysis, lightning_event, lightning_sample, line, line_tower, menu, object_group, rbac, scheduled_task, system_param, tower_model, tower_profile, user, wine, worker_registry
__all__ = [
"atp_asset",
"atp_model",
"audit_log",
"auth_session",
+156
View File
@@ -0,0 +1,156 @@
from __future__ import annotations
from datetime import datetime
from typing import Any
from uuid import uuid4
from sqlalchemy import JSON, Boolean, DateTime, ForeignKey, Index, Integer, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from ..core.database import Base
from .base import utcnow
class AtpAsset(Base):
__tablename__ = "atp_asset"
__table_args__ = (
UniqueConstraint("code", name="uq_atp_asset_code"),
Index("idx_atp_asset_status", "status"),
Index("idx_atp_asset_voltage_level", "voltage_level"),
Index("idx_atp_asset_tower_type", "tower_type"),
Index("idx_atp_asset_scene_type", "scene_type"),
)
id: Mapped[str] = mapped_column(String(32), primary_key=True, default=lambda: uuid4().hex)
code: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
name: Mapped[str] = mapped_column(String(255), nullable=False)
description: Mapped[str] = mapped_column(Text(), default="")
status: Mapped[str] = mapped_column(String(20), default="enabled", index=True)
voltage_level: Mapped[str | None] = mapped_column(String(16), index=True)
tower_type: Mapped[str | None] = mapped_column(String(64), index=True)
scene_type: Mapped[str | None] = mapped_column(String(32), index=True)
tags_json: Mapped[list[str]] = mapped_column(JSON, default=list)
latest_release_no: Mapped[int] = mapped_column(Integer, default=0)
active_release_no: Mapped[int | None] = mapped_column(Integer)
create_date: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, index=True)
create_user: Mapped[str | None] = mapped_column(String(64), index=True)
update_date: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, onupdate=utcnow)
update_user: Mapped[str | None] = mapped_column(String(64), index=True)
releases: Mapped[list[AtpAssetRelease]] = relationship(
"AtpAssetRelease",
back_populates="asset",
lazy="selectin",
cascade="all, delete-orphan",
order_by="AtpAssetRelease.release_no.desc()",
)
runs: Mapped[list[AtpAssetRun]] = relationship(
"AtpAssetRun",
back_populates="asset",
lazy="selectin",
cascade="all, delete-orphan",
order_by="AtpAssetRun.create_date.desc()",
)
class AtpAssetRelease(Base):
__tablename__ = "atp_asset_release"
__table_args__ = (
UniqueConstraint("asset_id", "release_no", name="uq_atp_asset_release_asset_no"),
Index("idx_atp_asset_release_status", "status"),
Index("idx_atp_asset_release_runner_kind", "runner_kind"),
Index("idx_atp_asset_release_storage_mount", "storage_mount_code"),
Index("idx_atp_asset_release_asset_active", "asset_id", "is_active"),
Index("idx_atp_asset_release_asset_status", "asset_id", "status"),
Index("idx_atp_asset_release_content_hash", "content_hash"),
)
id: Mapped[str] = mapped_column(String(32), primary_key=True, default=lambda: uuid4().hex)
asset_id: Mapped[str] = mapped_column(
String(32),
ForeignKey("atp_asset.id", ondelete="CASCADE"),
nullable=False,
index=True,
)
release_no: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
release_tag: Mapped[str | None] = mapped_column(String(64), index=True)
status: Mapped[str] = mapped_column(String(20), default="draft", index=True)
voltage_level: Mapped[str] = mapped_column(String(16), nullable=False, index=True)
tower_type: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
scene_type: Mapped[str] = mapped_column(String(32), nullable=False, index=True)
scenario_code: Mapped[str | None] = mapped_column(String(64), index=True)
runner_kind: Mapped[str] = mapped_column(String(20), default="atp", index=True)
storage_mount_code: Mapped[str] = mapped_column(String(64), default="main", index=True)
storage_root_path: Mapped[str] = mapped_column(String(2048), nullable=False)
entry_file: Mapped[str | None] = mapped_column(String(255))
result_file: Mapped[str | None] = mapped_column(String(255))
egm_subdir: Mapped[str | None] = mapped_column(String(255))
egm_result_file: Mapped[str | None] = mapped_column(String(255))
preprocess_script: Mapped[str | None] = mapped_column(String(255))
postprocess_script: Mapped[str | None] = mapped_column(String(255))
manifest_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
validation_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
content_hash: Mapped[str] = mapped_column(String(64), default="", index=True)
is_active: Mapped[bool] = mapped_column(Boolean, default=False, index=True)
create_date: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, index=True)
create_user: Mapped[str | None] = mapped_column(String(64), index=True)
update_date: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, onupdate=utcnow)
update_user: Mapped[str | None] = mapped_column(String(64), index=True)
asset: Mapped[AtpAsset] = relationship("AtpAsset", back_populates="releases", lazy="selectin")
runs: Mapped[list[AtpAssetRun]] = relationship(
"AtpAssetRun",
back_populates="release",
lazy="selectin",
cascade="all, delete-orphan",
order_by="AtpAssetRun.create_date.desc()",
)
class AtpAssetRun(Base):
__tablename__ = "atp_asset_run"
__table_args__ = (
Index("idx_atp_asset_run_status", "status"),
Index("idx_atp_asset_run_asset", "asset_id", "create_date"),
Index("idx_atp_asset_run_release", "release_id", "create_date"),
)
id: Mapped[str] = mapped_column(String(32), primary_key=True, default=lambda: uuid4().hex)
asset_id: Mapped[str] = mapped_column(
String(32),
ForeignKey("atp_asset.id", ondelete="CASCADE"),
nullable=False,
index=True,
)
release_id: Mapped[str] = mapped_column(
String(32),
ForeignKey("atp_asset_release.id", ondelete="CASCADE"),
nullable=False,
index=True,
)
status: Mapped[str] = mapped_column(String(20), default="pending", index=True)
engine_mode: Mapped[str] = mapped_column(String(20), default="wine", index=True)
runner_kind: Mapped[str] = mapped_column(String(20), default="atp", index=True)
task_id: Mapped[str | None] = mapped_column(String(128), index=True)
storage_mount_code: Mapped[str | None] = mapped_column(String(64), index=True)
storage_root_path: Mapped[str | None] = mapped_column(String(2048))
materialized_root_path: Mapped[str | None] = mapped_column(String(2048))
engine_command: Mapped[str | None] = mapped_column(String(2000))
working_dir: Mapped[str | None] = mapped_column(String(2000))
timeout_seconds: Mapped[int] = mapped_column(Integer, default=600)
exit_code: Mapped[int | None] = mapped_column(Integer)
started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
duration_ms: Mapped[int | None] = mapped_column(Integer)
stdout_text: Mapped[str | None] = mapped_column(Text())
stderr_text: Mapped[str | None] = mapped_column(Text())
output_manifest_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
result_summary_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
error_message: Mapped[str | None] = mapped_column(Text())
create_date: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, index=True)
create_user: Mapped[str | None] = mapped_column(String(64), index=True)
update_date: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, onupdate=utcnow)
update_user: Mapped[str | None] = mapped_column(String(64), index=True)
asset: Mapped[AtpAsset] = relationship("AtpAsset", back_populates="runs", lazy="selectin")
release: Mapped[AtpAssetRelease] = relationship("AtpAssetRelease", back_populates="runs", lazy="selectin")
+206
View File
@@ -0,0 +1,206 @@
from __future__ import annotations
from datetime import datetime
from typing import Any, Literal
from pydantic import BaseModel, Field
AtpAssetStatus = Literal["draft", "enabled", "disabled", "archived"]
AtpAssetReleaseStatus = Literal["draft", "released", "archived"]
AtpAssetRunnerKind = Literal["atp", "egm", "hybrid"]
AtpAssetRunStatus = Literal["pending", "running", "success", "failed"]
AtpAssetEngineMode = Literal["wine", "native"]
class AtpAssetSummary(BaseModel):
id: str
code: str
name: str
description: str
status: AtpAssetStatus
voltage_level: str | None = None
tower_type: str | None = None
scene_type: str | None = None
tags_json: list[str] = Field(default_factory=list)
latest_release_no: int = 0
active_release_no: int | None = None
active_release_id: str | None = None
active_release_tag: str | None = None
release_count: int = 0
run_count: int = 0
last_run_status: AtpAssetRunStatus | None = None
last_run_date: datetime | None = None
create_date: datetime
create_user: str | None = None
update_date: datetime
update_user: str | None = None
class AtpAssetListResponse(BaseModel):
items: list[AtpAssetSummary]
total: int
class AtpAssetDetail(AtpAssetSummary):
pass
class AtpAssetCreateRequest(BaseModel):
code: str = Field(min_length=1, max_length=64)
name: str = Field(min_length=1, max_length=255)
description: str = Field(default="", max_length=8000)
status: AtpAssetStatus = "enabled"
voltage_level: str | None = Field(default=None, max_length=16)
tower_type: str | None = Field(default=None, max_length=64)
scene_type: str | None = Field(default=None, max_length=32)
tags_json: list[str] = Field(default_factory=list, max_length=128)
class AtpAssetUpdateRequest(BaseModel):
name: str | None = Field(default=None, min_length=1, max_length=255)
description: str | None = Field(default=None, max_length=8000)
status: AtpAssetStatus | None = None
voltage_level: str | None = Field(default=None, max_length=16)
tower_type: str | None = Field(default=None, max_length=64)
scene_type: str | None = Field(default=None, max_length=32)
tags_json: list[str] | None = Field(default=None, max_length=128)
class AtpAssetReleaseSummary(BaseModel):
id: str
asset_id: str
asset_code: str
asset_name: str
release_no: int
release_tag: str | None = None
status: AtpAssetReleaseStatus
voltage_level: str
tower_type: str
scene_type: str
scenario_code: str | None = None
runner_kind: AtpAssetRunnerKind
storage_mount_code: str
storage_root_path: str
entry_file: str | None = None
result_file: str | None = None
egm_subdir: str | None = None
egm_result_file: str | None = None
preprocess_script: str | None = None
postprocess_script: str | None = None
content_hash: str
is_active: bool
create_date: datetime
create_user: str | None = None
update_date: datetime
update_user: str | None = None
class AtpAssetReleaseDetail(AtpAssetReleaseSummary):
manifest_json: dict[str, Any] = Field(default_factory=dict)
validation_json: dict[str, Any] = Field(default_factory=dict)
class AtpAssetReleaseListResponse(BaseModel):
items: list[AtpAssetReleaseSummary]
total: int
class AtpAssetReleaseCreateRequest(BaseModel):
release_tag: str | None = Field(default=None, max_length=64)
status: AtpAssetReleaseStatus = "released"
voltage_level: str = Field(min_length=1, max_length=16)
tower_type: str = Field(min_length=1, max_length=64)
scene_type: str = Field(min_length=1, max_length=32)
scenario_code: str | None = Field(default=None, max_length=64)
runner_kind: AtpAssetRunnerKind = "atp"
storage_mount_code: str = Field(default="main", min_length=1, max_length=64)
storage_root_path: str = Field(min_length=1, max_length=2048)
entry_file: str | None = Field(default=None, max_length=255)
result_file: str | None = Field(default=None, max_length=255)
egm_subdir: str | None = Field(default=None, max_length=255)
egm_result_file: str | None = Field(default=None, max_length=255)
preprocess_script: str | None = Field(default=None, max_length=255)
postprocess_script: str | None = Field(default=None, max_length=255)
class AtpAssetReleaseUpdateRequest(BaseModel):
release_tag: str | None = Field(default=None, max_length=64)
status: AtpAssetReleaseStatus | None = None
voltage_level: str | None = Field(default=None, min_length=1, max_length=16)
tower_type: str | None = Field(default=None, min_length=1, max_length=64)
scene_type: str | None = Field(default=None, min_length=1, max_length=32)
scenario_code: str | None = Field(default=None, max_length=64)
runner_kind: AtpAssetRunnerKind | None = None
storage_mount_code: str | None = Field(default=None, min_length=1, max_length=64)
storage_root_path: str | None = Field(default=None, min_length=1, max_length=2048)
entry_file: str | None = Field(default=None, max_length=255)
result_file: str | None = Field(default=None, max_length=255)
egm_subdir: str | None = Field(default=None, max_length=255)
egm_result_file: str | None = Field(default=None, max_length=255)
preprocess_script: str | None = Field(default=None, max_length=255)
postprocess_script: str | None = Field(default=None, max_length=255)
class AtpAssetFileEntry(BaseModel):
relative_path: str
name: str
is_dir: bool
size: int = 0
mime_type: str | None = None
file_role: str | None = None
class AtpAssetFileListResponse(BaseModel):
release_id: str
storage_mount_code: str
storage_root_path: str
items: list[AtpAssetFileEntry]
total: int
class AtpAssetRunSummary(BaseModel):
id: str
asset_id: str
asset_code: str
asset_name: str
release_id: str
release_no: int
release_tag: str | None = None
status: AtpAssetRunStatus
engine_mode: AtpAssetEngineMode
runner_kind: AtpAssetRunnerKind
task_id: str | None = None
storage_mount_code: str | None = None
storage_root_path: str | None = None
materialized_root_path: str | None = None
engine_command: str | None = None
working_dir: str | None = None
timeout_seconds: int
exit_code: int | None = None
started_at: datetime | None = None
finished_at: datetime | None = None
duration_ms: int | None = None
error_message: str | None = None
stdout_size: int = 0
stderr_size: int = 0
create_date: datetime
create_user: str | None = None
class AtpAssetRunDetail(AtpAssetRunSummary):
stdout_text: str | None = None
stderr_text: str | None = None
output_manifest_json: dict[str, Any] = Field(default_factory=dict)
result_summary_json: dict[str, Any] = Field(default_factory=dict)
class AtpAssetRunListResponse(BaseModel):
items: list[AtpAssetRunSummary]
total: int
class AtpAssetRunRequest(BaseModel):
timeout_seconds: int | None = Field(default=None, ge=1)
extra_args: list[str] = Field(default_factory=list, max_length=32)
environment: dict[str, str] = Field(default_factory=dict, max_length=16)
dry_run: bool = False
File diff suppressed because it is too large Load Diff
+13
View File
@@ -0,0 +1,13 @@
from __future__ import annotations
from ..core.celery_app import celery_app
from ..services.atp_asset_service import execute_asset_run_job
@celery_app.task(name="app.tasks.atp_asset_tasks.execute_atp_asset_run_job")
def execute_atp_asset_run_job(
run_id: str,
payload_data: dict,
actor_user_id: str | None,
) -> None:
execute_asset_run_job(run_id=run_id, payload_data=payload_data, actor_user_id=actor_user_id)