feat: 更新 Dockerfile 和 docker-compose.yml,使用清华镜像源,升级 CVAT 版本,优化环境配置
fix: 清理飞书配置占位符,增强错误处理,确保前端不暴露内部错误信息 feat: 在标注服务中添加上传错误处理,优化 CVAT 状态查询 feat: 更新 AnnotationPage 以显示上传状态和错误信息,增强用户体验 feat: 登录页面添加重定向功能,优化用户登录流程 refactor: 删除 tsconfig.tsbuildinfo 文件,清理不必要的构建信息
This commit is contained in:
@@ -29,7 +29,10 @@ STATE_EXPIRE_MINUTES = 10
|
||||
|
||||
|
||||
def is_feishu_configured() -> bool:
|
||||
return bool(FEISHU_APP_ID and FEISHU_APP_SECRET)
|
||||
"""飞书是否已配置真实 App ID/Secret(排除占位符)。"""
|
||||
if not FEISHU_APP_ID or not FEISHU_APP_SECRET:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def build_authorize_url() -> tuple[str, str]:
|
||||
|
||||
@@ -121,7 +121,9 @@ def campaign_bootstrap(campaign_id: str) -> dict[str, Any]:
|
||||
row["batch_error"] = str(e)
|
||||
row["editor"] = "cvat"
|
||||
row["cvat_task_id"] = camp.cvat_task_id
|
||||
row["cvat_job_url"] = camp.cvat_job_url
|
||||
# 清理内部错误标记(以 _ 开头表示上传失败),不暴露给前端
|
||||
raw_url = camp.cvat_job_url or ""
|
||||
row["cvat_job_url"] = None if raw_url.startswith("_") else camp.cvat_job_url
|
||||
return row
|
||||
|
||||
|
||||
|
||||
@@ -33,6 +33,16 @@ def _campaign_id(project: str, task: str, mode: str | None, batch: str, location
|
||||
return hashlib.sha256(raw.encode()).hexdigest()[:20]
|
||||
|
||||
|
||||
def _sanitize_campaign_row(row: dict) -> dict:
|
||||
"""清理内部错误标记(cvat_job_url 以 _ 开头表示上传失败),避免前端误用。"""
|
||||
url = row.get("cvat_job_url") or ""
|
||||
if isinstance(url, str) and url.startswith("_"):
|
||||
row["cvat_job_url"] = None
|
||||
# 提取错误信息到专用字段
|
||||
row["upload_error"] = url.lstrip("_").replace("_", " ", 1)
|
||||
return row
|
||||
|
||||
|
||||
def _parse_scope_key(scope_key: str) -> tuple[str, str, str | None]:
|
||||
parts = scope_key.split(":")
|
||||
if parts[0] == "lane":
|
||||
@@ -190,10 +200,19 @@ def open_campaign(
|
||||
images = _iter_batch_images(batch_dir)
|
||||
if images:
|
||||
import threading
|
||||
cvat_uploader = _cvat_upload_thread(cvat_task_id, images)
|
||||
cvat_uploader = _cvat_upload_thread(cvat_task_id, images, campaign_id=cid)
|
||||
threading.Thread(target=cvat_uploader, daemon=True).start()
|
||||
except Exception:
|
||||
pass
|
||||
camp.cvat_job_url = None # 尚未就绪,等待上传线程回填
|
||||
else:
|
||||
print(f"[CVAT] open_campaign: 未找到图片 batch_dir={batch_dir} campaign={cid}")
|
||||
camp.cvat_job_url = "_NO_IMAGES_: 未找到图片"
|
||||
camp.status = "upload_failed"
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print(f"[CVAT] open_campaign: 准备上传失败 {e}")
|
||||
camp.cvat_job_url = f"_UPLOAD_SETUP_FAILED_: {e}"
|
||||
camp.status = "upload_failed"
|
||||
|
||||
update_campaign_batch_meta_stage(camp, "out_for_labeling")
|
||||
reg = load_dms_registry() if project == "dms" else None
|
||||
@@ -229,7 +248,7 @@ def get_campaign(campaign_id: str) -> dict[str, Any] | None:
|
||||
return None
|
||||
row = camp.to_dict()
|
||||
reg = load_dms_registry() if row.get("project") == "dms" else None
|
||||
return enrich_batch_labels(row, reg)
|
||||
return _sanitize_campaign_row(enrich_batch_labels(row, reg))
|
||||
|
||||
|
||||
def _export_job_id() -> str:
|
||||
@@ -405,7 +424,7 @@ def assign_campaign(campaign_id: str, user_id: int | None) -> dict[str, Any]:
|
||||
db.flush()
|
||||
out = camp.to_dict()
|
||||
reg = load_dms_registry() if out.get("project") == "dms" else None
|
||||
return enrich_batch_labels(out, reg)
|
||||
return _sanitize_campaign_row(enrich_batch_labels(out, reg))
|
||||
|
||||
|
||||
def submit_campaign(campaign_id: str) -> dict[str, Any]:
|
||||
@@ -496,15 +515,45 @@ def _resolve_default_annotation_types(project: str, task: str | None, mode: str
|
||||
return resolve_annotation_types(project, task, mode)
|
||||
|
||||
|
||||
def _cvat_upload_thread(cvat_task_id: int, image_paths: list):
|
||||
"""在线程中上传图片到 CVAT。"""
|
||||
def _cvat_upload_thread(cvat_task_id: int, image_paths: list, campaign_id: str | None = None):
|
||||
"""在线程中上传图片到 CVAT,失败时将错误写入 campaign 的 cvat_job_url。"""
|
||||
def _run():
|
||||
updated = False
|
||||
try:
|
||||
from as_platform.labeling.cvat_client import get_cvat_client
|
||||
cvat = get_cvat_client()
|
||||
cvat.upload_images(cvat_task_id, image_paths)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
if campaign_id:
|
||||
try:
|
||||
with session_scope() as db:
|
||||
camp = db.get(LabelingCampaign, campaign_id)
|
||||
if camp and camp.cvat_job_url is None:
|
||||
camp.cvat_job_url = f"_UPLOAD_FAILED_: {e}"
|
||||
camp.status = "upload_failed"
|
||||
updated = True
|
||||
except Exception:
|
||||
pass
|
||||
print(f"[CVAT] 图片上传失败 task={cvat_task_id} campaign={campaign_id}: {e}")
|
||||
else:
|
||||
if campaign_id:
|
||||
try:
|
||||
with session_scope() as db:
|
||||
camp = db.get(LabelingCampaign, campaign_id)
|
||||
# 上传成功后,尝试回填 job_url
|
||||
if camp and camp.cvat_job_url is None:
|
||||
from as_platform.labeling.cvat_client import get_cvat_client
|
||||
cvat = get_cvat_client()
|
||||
task = cvat.get_task(cvat_task_id)
|
||||
if task.job_url:
|
||||
camp.cvat_job_url = task.job_url
|
||||
camp.status = "in_progress"
|
||||
updated = True
|
||||
except Exception:
|
||||
pass
|
||||
print(f"[CVAT] 图片上传完成 task={cvat_task_id} campaign={campaign_id}")
|
||||
return _run
|
||||
|
||||
|
||||
@@ -608,7 +657,7 @@ def _build_class_map(camp, reg: dict | None) -> dict[str, int]:
|
||||
|
||||
|
||||
def get_cvat_status(campaign_id: str) -> dict[str, Any]:
|
||||
"""查询 CVAT 侧 Task 状态。"""
|
||||
"""查询 CVAT 侧 Task 状态,包含上传进度信息。"""
|
||||
with session_scope() as db:
|
||||
camp = db.get(LabelingCampaign, campaign_id)
|
||||
if not camp:
|
||||
@@ -616,16 +665,39 @@ def get_cvat_status(campaign_id: str) -> dict[str, Any]:
|
||||
if not camp.cvat_task_id:
|
||||
return {"cvat_available": False, "campaign_id": campaign_id}
|
||||
|
||||
# 检测内部错误标记(cvat_job_url 以 _ 开头表示错误)
|
||||
cvat_job_url = camp.cvat_job_url or ""
|
||||
if cvat_job_url.startswith("_"):
|
||||
# 错误状态:提取错误信息
|
||||
error_msg = cvat_job_url.lstrip("_").replace("_", " ", 1) if cvat_job_url.startswith("_") else cvat_job_url
|
||||
return {
|
||||
"cvat_available": True,
|
||||
"campaign_id": campaign_id,
|
||||
"cvat_task_id": camp.cvat_task_id,
|
||||
"cvat_job_url": None,
|
||||
"cvat_status": camp.status or "unknown",
|
||||
"upload_error": error_msg,
|
||||
}
|
||||
|
||||
from as_platform.labeling.cvat_client import get_cvat_client
|
||||
cvat = get_cvat_client()
|
||||
try:
|
||||
task = cvat.get_task(camp.cvat_task_id)
|
||||
# 尝试统计图片数量
|
||||
image_count = 0
|
||||
try:
|
||||
batch_dir = resolve_campaign_batch_dir(camp)
|
||||
images = _iter_batch_images(batch_dir)
|
||||
image_count = len(images)
|
||||
except Exception:
|
||||
pass
|
||||
return {
|
||||
"cvat_available": True,
|
||||
"campaign_id": campaign_id,
|
||||
"cvat_task_id": camp.cvat_task_id,
|
||||
"cvat_job_url": task.job_url or camp.cvat_job_url,
|
||||
"cvat_status": task.status,
|
||||
"image_count": image_count,
|
||||
}
|
||||
except Exception as e:
|
||||
return {"cvat_available": False, "campaign_id": campaign_id, "error": str(e)}
|
||||
|
||||
Reference in New Issue
Block a user