[fix/feat]:[FL-81][ATP模型管理改造为资产发布制]
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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")
|
||||
@@ -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
@@ -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)
|
||||
Reference in New Issue
Block a user