Deploy v4.6.9 manual task center
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- README.md +1 -1
- VERSION +1 -1
- routes/manual_tasks.py +66 -0
- services/manual_tasks.py +109 -0
- static/js/app.js +53 -6
- tests/test_api_entry_preflight_v456.py +1 -1
- tests/test_chat_lifecycle_progress_v393.py +1 -1
- tests/test_clarification_ui_v454.py +1 -1
- tests/test_classification_cleanup_v466.py +1 -1
- tests/test_context_and_multitask_finalize_v451.py +1 -1
- tests/test_download_host_authorization_v460.py +1 -1
- tests/test_download_opfs_fallback_v459.py +1 -1
- tests/test_download_progress_v391.py +1 -1
- tests/test_download_resilience_v422.py +1 -1
- tests/test_frontend_preflight_v457.py +1 -1
- tests/test_history_sidebar_v373.py +1 -1
- tests/test_large_download_jobs_v400.py +1 -1
- tests/test_manual_tasks_v469.py +30 -0
- tests/test_merged_health_v433.py +1 -1
- tests/test_multitask_observability_v442.py +1 -1
- tests/test_multitask_sse_finalize_v452.py +1 -1
- tests/test_noaa_ramldb_roles_v465.py +1 -1
- tests/test_ocean_batch_downloads_v420.py +1 -1
- tests/test_ocean_batch_routing_v421.py +1 -1
- tests/test_ocean_batch_v410.py +1 -1
- tests/test_ocean_date_default_v453.py +1 -1
- tests/test_ocean_download_url_base_v462.py +1 -1
- tests/test_p0_finish_v380.py +1 -1
- tests/test_p0_phase2_v371.py +1 -1
- tests/test_p0_phase3_v372.py +1 -1
- tests/test_platform_reliability_v450.py +1 -1
- tests/test_quality_center_v362.py +1 -1
- tests/test_quality_trust_v361.py +1 -1
- tests/test_reference_cleanup_v468.py +1 -1
- tests/test_release_stability.py +1 -1
- tests/test_resumable_batch_download_v458.py +1 -1
- tests/test_route_region_clarification_v455.py +1 -1
- tests/test_runtime_hotfix_v376.py +1 -1
- tests/test_sidebar_collapse_v374.py +1 -1
- tests/test_sidebar_partial_collapse_v375.py +1 -1
- tests/test_smart_progress_avatar_sync_v441.py +1 -1
- tests/test_source_mapping_roles_v464.py +1 -1
- tests/test_squid_finalize_v467.py +1 -1
- tests/test_sse_heartbeat_v401.py +1 -1
- tests/test_ui_avatar_multitask_v440.py +1 -1
- tests/test_unclassified_inventory_v463.py +1 -1
- tests/test_unified_download_manager_v392.py +1 -1
- tests/test_unified_query_v390.py +1 -1
- tests/test_upstream_refresh_retry_v461.py +1 -1
- tests/test_v381_navigation_ocean_intent.py +1 -1
README.md
CHANGED
|
@@ -10,7 +10,7 @@ pinned: false
|
|
| 10 |
|
| 11 |
# Global Marine Foundation Data Agent
|
| 12 |
|
| 13 |
-
Current UI release: **v4.6.
|
| 14 |
|
| 15 |
|
| 16 |
## v3.4.0 稳定性重构(第一阶段)
|
|
|
|
| 10 |
|
| 11 |
# Global Marine Foundation Data Agent
|
| 12 |
|
| 13 |
+
Current UI release: **v4.6.9**.
|
| 14 |
|
| 15 |
|
| 16 |
## v3.4.0 稳定性重构(第一阶段)
|
VERSION
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
4.6.
|
|
|
|
| 1 |
+
4.6.9
|
routes/manual_tasks.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
from fastapi import HTTPException, Request
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def _inject(context):
|
| 6 |
+
globals().update(context)
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
async def create_manual_task(request:Request):
|
| 10 |
+
try:
|
| 11 |
+
body=await request.json()
|
| 12 |
+
except Exception:
|
| 13 |
+
raise HTTPException(400,"invalid json")
|
| 14 |
+
uid,_=await resolve_request_user(request,str(body.get("user_id") or ""))
|
| 15 |
+
try:
|
| 16 |
+
task=MANUAL_TASK_STORE.create(
|
| 17 |
+
uid,
|
| 18 |
+
body.get("title"),
|
| 19 |
+
body.get("description") or "",
|
| 20 |
+
body.get("status") or "pending",
|
| 21 |
+
)
|
| 22 |
+
except ValueError as exc:
|
| 23 |
+
raise HTTPException(400,str(exc))
|
| 24 |
+
try:
|
| 25 |
+
await safe_memory_event(uid,"manual_task_created",{"task_id":task["task_id"],"title":task["title"],"status":task["status"]})
|
| 26 |
+
except Exception:
|
| 27 |
+
pass
|
| 28 |
+
return {"status":"ok","task":task,"storage":MANUAL_TASK_STORE.storage_mode}
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
async def update_manual_task(task_id:str,request:Request):
|
| 32 |
+
try:
|
| 33 |
+
body=await request.json()
|
| 34 |
+
except Exception:
|
| 35 |
+
raise HTTPException(400,"invalid json")
|
| 36 |
+
uid,_=await resolve_request_user(request,str(body.get("user_id") or ""))
|
| 37 |
+
try:
|
| 38 |
+
task=MANUAL_TASK_STORE.update(uid,task_id,body)
|
| 39 |
+
except ValueError as exc:
|
| 40 |
+
raise HTTPException(400,str(exc))
|
| 41 |
+
if not task:
|
| 42 |
+
raise HTTPException(404,"manual task not found")
|
| 43 |
+
try:
|
| 44 |
+
await safe_memory_event(uid,"manual_task_updated",{"task_id":task_id,"status":task.get("status")})
|
| 45 |
+
except Exception:
|
| 46 |
+
pass
|
| 47 |
+
return {"status":"ok","task":task,"storage":MANUAL_TASK_STORE.storage_mode}
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
async def delete_manual_task(task_id:str,request:Request):
|
| 51 |
+
supplied=str(request.query_params.get("user_id") or "").strip()
|
| 52 |
+
uid,_=await resolve_request_user(request,supplied)
|
| 53 |
+
if not MANUAL_TASK_STORE.delete(uid,task_id):
|
| 54 |
+
raise HTTPException(404,"manual task not found")
|
| 55 |
+
try:
|
| 56 |
+
await safe_memory_event(uid,"manual_task_deleted",{"task_id":task_id})
|
| 57 |
+
except Exception:
|
| 58 |
+
pass
|
| 59 |
+
return {"status":"ok","task_id":task_id}
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def register_manual_task_routes(app,context):
|
| 63 |
+
_inject(context)
|
| 64 |
+
app.add_api_route("/api/sidebar/tasks/manual",create_manual_task,methods=["POST"])
|
| 65 |
+
app.add_api_route("/api/sidebar/tasks/manual/{task_id}",update_manual_task,methods=["PATCH"])
|
| 66 |
+
app.add_api_route("/api/sidebar/tasks/manual/{task_id}",delete_manual_task,methods=["DELETE"])
|
services/manual_tasks.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
import secrets
|
| 5 |
+
import time
|
| 6 |
+
from datetime import datetime
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from threading import RLock
|
| 9 |
+
from typing import Any
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class ManualTaskStore:
|
| 13 |
+
VALID_STATUSES={"pending","running","completed","failed"}
|
| 14 |
+
|
| 15 |
+
def __init__(self, root:Path, valid_user_id, max_tasks:int=300):
|
| 16 |
+
self.root=Path(root)/"manual_tasks"
|
| 17 |
+
self.root.mkdir(parents=True,exist_ok=True)
|
| 18 |
+
self.valid_user_id=valid_user_id
|
| 19 |
+
self.max_tasks=max(20,int(max_tasks))
|
| 20 |
+
self.lock=RLock()
|
| 21 |
+
|
| 22 |
+
@property
|
| 23 |
+
def storage_mode(self)->str:
|
| 24 |
+
return "persistent" if str(self.root).startswith("/data/") else "server-session"
|
| 25 |
+
|
| 26 |
+
def _file(self,user_id:str)->Path:
|
| 27 |
+
if not self.valid_user_id(user_id):
|
| 28 |
+
raise ValueError("invalid user_id")
|
| 29 |
+
return self.root/f"{user_id}.json"
|
| 30 |
+
|
| 31 |
+
def _read(self,user_id:str)->list[dict[str,Any]]:
|
| 32 |
+
path=self._file(user_id)
|
| 33 |
+
try:
|
| 34 |
+
raw=json.loads(path.read_text(encoding="utf-8"))
|
| 35 |
+
return raw if isinstance(raw,list) else []
|
| 36 |
+
except Exception:
|
| 37 |
+
return []
|
| 38 |
+
|
| 39 |
+
def _write(self,user_id:str,tasks:list[dict[str,Any]])->None:
|
| 40 |
+
path=self._file(user_id)
|
| 41 |
+
tmp=path.with_suffix(".tmp")
|
| 42 |
+
tmp.write_text(json.dumps(tasks[:self.max_tasks],ensure_ascii=False,indent=2),encoding="utf-8")
|
| 43 |
+
tmp.replace(path)
|
| 44 |
+
|
| 45 |
+
def _clean_text(self,value:Any,limit:int)->str:
|
| 46 |
+
return " ".join(str(value or "").replace("\x00","").split())[:limit]
|
| 47 |
+
|
| 48 |
+
def _normalize_status(self,value:Any)->str:
|
| 49 |
+
status=str(value or "pending").strip().lower()
|
| 50 |
+
return status if status in self.VALID_STATUSES else "pending"
|
| 51 |
+
|
| 52 |
+
def list_user(self,user_id:str,limit:int=200)->list[dict[str,Any]]:
|
| 53 |
+
with self.lock:
|
| 54 |
+
tasks=self._read(user_id)
|
| 55 |
+
tasks.sort(key=lambda x:str(x.get("updated_at") or x.get("created_at") or ""),reverse=True)
|
| 56 |
+
return tasks[:max(1,min(int(limit or 200),self.max_tasks))]
|
| 57 |
+
|
| 58 |
+
def create(self,user_id:str,title:str,description:str="",status:str="pending")->dict[str,Any]:
|
| 59 |
+
now=datetime.now().astimezone().isoformat(timespec="seconds")
|
| 60 |
+
task={
|
| 61 |
+
"kind":"manual",
|
| 62 |
+
"task_id":"manual_"+secrets.token_hex(8),
|
| 63 |
+
"user_id":user_id,
|
| 64 |
+
"title":self._clean_text(title,160),
|
| 65 |
+
"description":self._clean_text(description,4000),
|
| 66 |
+
"status":self._normalize_status(status),
|
| 67 |
+
"source":"manual",
|
| 68 |
+
"created_at":now,
|
| 69 |
+
"updated_at":now,
|
| 70 |
+
}
|
| 71 |
+
if not task["title"]:
|
| 72 |
+
raise ValueError("title is required")
|
| 73 |
+
with self.lock:
|
| 74 |
+
tasks=self._read(user_id)
|
| 75 |
+
tasks=[task]+[x for x in tasks if x.get("task_id")!=task["task_id"]]
|
| 76 |
+
self._write(user_id,tasks)
|
| 77 |
+
return task
|
| 78 |
+
|
| 79 |
+
def update(self,user_id:str,task_id:str,patch:dict[str,Any])->dict[str,Any]|None:
|
| 80 |
+
with self.lock:
|
| 81 |
+
tasks=self._read(user_id)
|
| 82 |
+
target=None
|
| 83 |
+
for task in tasks:
|
| 84 |
+
if str(task.get("task_id") or "")==task_id:
|
| 85 |
+
target=task
|
| 86 |
+
break
|
| 87 |
+
if target is None:
|
| 88 |
+
return None
|
| 89 |
+
if "title" in patch:
|
| 90 |
+
title=self._clean_text(patch.get("title"),160)
|
| 91 |
+
if not title:
|
| 92 |
+
raise ValueError("title is required")
|
| 93 |
+
target["title"]=title
|
| 94 |
+
if "description" in patch:
|
| 95 |
+
target["description"]=self._clean_text(patch.get("description"),4000)
|
| 96 |
+
if "status" in patch:
|
| 97 |
+
target["status"]=self._normalize_status(patch.get("status"))
|
| 98 |
+
target["updated_at"]=datetime.now().astimezone().isoformat(timespec="seconds")
|
| 99 |
+
self._write(user_id,tasks)
|
| 100 |
+
return dict(target)
|
| 101 |
+
|
| 102 |
+
def delete(self,user_id:str,task_id:str)->bool:
|
| 103 |
+
with self.lock:
|
| 104 |
+
tasks=self._read(user_id)
|
| 105 |
+
kept=[x for x in tasks if str(x.get("task_id") or "")!=task_id]
|
| 106 |
+
if len(kept)==len(tasks):
|
| 107 |
+
return False
|
| 108 |
+
self._write(user_id,kept)
|
| 109 |
+
return True
|
static/js/app.js
CHANGED
|
@@ -1087,8 +1087,8 @@ async function renderServices(){
|
|
| 1087 |
|
| 1088 |
const TASK_OPERATION_ZH={quality_check:"数据质量检查",marine_export:"海洋数据导出",marine_query:"海洋数据查询",marine_subset:"海洋区域裁剪",fisheries_query:"渔业数据查询",task:"数据处理任务"};
|
| 1089 |
function taskOperationLabel(v){return TASK_OPERATION_ZH[String(v||"")]||String(v||"数据处理任务")}
|
| 1090 |
-
function taskStatusGroup(v){const s=String(v||"");if(/completed|done|success|成功|完成/i.test(s))return "completed";if(/failed|error|失败|异常/i.test(s))return "failed";return "running"}
|
| 1091 |
-
function taskStatusLabel(v){const g=taskStatusGroup(v);return g==="completed"?"已完成":g==="failed"?"失败":"处理中"}
|
| 1092 |
function taskProgressHtml(task){
|
| 1093 |
const g=taskStatusGroup(task.status),explicit=Number(task.progress);
|
| 1094 |
if(task.progress!==null&&task.progress!==undefined&&Number.isFinite(explicit))return '<div class="task-progress" title="进度 '+esc(explicit)+'%"><span style="width:'+Math.max(0,Math.min(100,explicit))+'%"></span></div><div class="row-meta">处理进度:'+esc(explicit)+'%</div>';
|
|
@@ -1119,21 +1119,68 @@ function assetContinuePrompt(asset){return '请分析这个已生成的数据资
|
|
| 1119 |
function openAssetDetail(asset){
|
| 1120 |
document.querySelector(".drawer-backdrop")?.remove();const wrap=document.createElement("div");wrap.className="drawer-backdrop";wrap.innerHTML='<aside class="detail-drawer" role="dialog" aria-modal="true" aria-label="数据资产详情"><div class="drawer-head"><div><h2>'+esc(asset.name||"未命名资产")+'</h2><p>'+esc(taskOperationLabel(asset.operation))+' · '+esc(taskStatusLabel(asset.status))+'</p></div><button class="drawer-close" aria-label="关闭">×</button></div><div class="task-summary"><div><span>资产编号</span><b>'+esc(asset.asset_id||"-")+'</b></div><div><span>文件大小</span><b>'+esc(formatBytes(asset.size_bytes))+'</b></div><div><span>文件类型</span><b>'+esc(asset.mime_type||"-")+'</b></div><div><span>创建时间</span><b>'+esc(formatTime(asset.created_at))+'</b></div></div><div class="detail-section"><h3>资产来源</h3><div class="row-detail">来源:'+esc(asset.source||"未记录")+(asset.path?'<br>路径:'+esc(asset.path):'')+(asset.thread_id?'<br>关联会话:'+esc(asset.thread_id):'')+'</div></div><div class="card-actions">'+(asset.download_url?'<a class="download" href="'+esc(asset.download_url)+'" target="_blank" rel="noopener">↓ 下载资产</a>':'')+'<button class="view-btn primary asset-chat">在对话中分析</button></div></aside>';document.body.appendChild(wrap);const close=()=>wrap.remove();wrap.querySelector(".drawer-close").onclick=close;wrap.onclick=e=>{if(e.target===wrap)close()};wrap.querySelector(".asset-chat").onclick=()=>{close();goChat(assetContinuePrompt(asset))}
|
| 1121 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1122 |
async function renderTasks(){
|
| 1123 |
-
const root=$("workspaceInner");root.innerHTML='<div class="view-head"><div><h1>我的任务</h1><p>查看当前登录用户的数据处理任务和生成的数据资产。</p></div><button id="refreshTasks" class="view-btn">↻ 刷新</button></div><div class="loading">正在读取任务记录…</div>';$("refreshTasks").onclick=renderTasks;
|
| 1124 |
try{
|
| 1125 |
-
const data=await j("/api/sidebar/tasks?user_id="+encodeURIComponent(USER_ID)),tasks=data.tasks||[],assets=data.assets||[],localTasks=data.local_tasks||[],allStatus=[...tasks,...localTasks],
|
| 1126 |
-
let html='<div class="view-head"><div><h1>我的任务</h1><p>'+esc(data.enabled?"任务、结果和数据资产已按当前用户关联":"任务记录服务尚未启用")+'</p></div><button id="refreshTasks" class="view-btn">↻ 刷新</button></div><div class="summary-grid"><div class="summary-card"><strong>'+(tasks.length+localTasks.length)+'</strong><span>全部任务</span></div><div class="summary-card"><strong>'+running+'</strong><span>处理中</span></div><div class="summary-card"><strong>'+completed+'</strong><span>已完成</span></div><div class="summary-card"><strong>'+failed+'</strong><span>失败</span></div><div class="summary-card"><strong>'+assets.length+'</strong><span>数据资产</span></div></div>';
|
| 1127 |
if(data.error)html+='<div class="service-row"><div class="row-detail">'+esc(data.error)+'</div></div>';
|
| 1128 |
html+='<h3 style="font-size:14px;margin:20px 0 10px">持久化任务</h3><div id="persistentTaskList"></div><div class="filter-bar"><input id="taskSearch" type="search" placeholder="搜索任务类型、编号、来源或关联文件"><select id="taskStatus"><option value="all">全部状态</option><option value="running">处理中</option><option value="completed">已完成</option><option value="failed">失败</option></select><span id="taskCount" class="filter-count"></span></div><h3 style="font-size:14px;margin:20px 0 10px">处理任务</h3><div id="taskList"></div><div class="filter-bar" style="margin-top:20px"><input id="assetSearch" type="search" placeholder="搜索资产名称、类型或来源"><span id="assetCount" class="filter-count"></span></div><h3 style="font-size:14px;margin:12px 0 10px">数据资产</h3><div id="assetList"></div>';
|
| 1129 |
-
root.innerHTML=html;$("refreshTasks").onclick=renderTasks;
|
| 1130 |
const drawPersistent=()=>{
|
| 1131 |
const box=$("persistentTaskList");if(!box)return;
|
| 1132 |
if(!localTasks.length){box.innerHTML='<div class="empty-view"><b>暂无持久化任务</b>Ocean 批量导出和大文件下载任务会保存在这里。</div>';return}
|
| 1133 |
box.innerHTML=localTasks.map((x,i)=>{
|
|
|
|
| 1134 |
if(x.kind==="ocean_batch"){const c=x.counts||{},pct=Number(x.progress_percent||0);return '<div class="task-row" data-local-index="'+i+'"><div class="task-head"><b>Ocean 批量导出</b><span class="badge '+(x.status==="completed"?"ok":x.status==="partial"?"bad":"warn")+'">'+esc(x.status||"-")+'</span></div><div class="row-meta">'+esc(x.job_id||"")+' · '+esc(formatTime(x.updated_at||x.created_at))+(x.thread_id?' · 关联会话 '+esc(x.thread_id):'')+'</div><div class="row-detail">'+esc(x.prompt||"")+'</div><div class="ocean-batch-track"><span style="width:'+Math.min(100,pct)+'%"></span></div><div class="row-meta">'+pct.toFixed(0)+'% · 完成 '+esc(c.completed||0)+' / '+esc(x.total||0)+' · 失败 '+esc(c.failed||0)+'</div><div class="card-actions">'+(x.status==="paused"?'<button class="view-btn local-resume">继续任务</button>':'')+((c.failed||0)>0?'<button class="view-btn local-retry">仅重试失败项</button>':'')+'<button class="view-btn local-detail">查看子任务</button>'+(x.thread_id?'<button class="view-btn primary local-chat">回到关联对话</button>':'')+'</div><div class="local-subtasks" hidden></div></div>'}
|
| 1135 |
return '<div class="task-row"><div class="task-head"><b>大文件下载</b><span class="badge '+(x.status==="completed"?"ok":x.status==="failed"?"bad":"warn")+'">'+esc(x.status||"-")+'</span></div><div class="row-meta">'+esc(x.job_id||"")+' · '+esc(x.filename||"download")+' · '+esc(formatTime(x.updated_at))+'</div><div class="row-detail">已传输 '+esc(formatBytes(x.transferred_bytes||0))+(x.total_bytes?' / '+esc(formatBytes(x.total_bytes)):'')+(x.resume_supported?' · 支持断点续传':'')+'</div></div>'
|
| 1136 |
}).join('');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1137 |
box.querySelectorAll('[data-local-index]').forEach(card=>{const x=localTasks[Number(card.dataset.localIndex)];
|
| 1138 |
card.querySelector('.local-resume')?.addEventListener('click',async()=>{await j('/api/ocean/batch/'+encodeURIComponent(x.job_id)+'/resume',{method:'POST'});renderTasks()});
|
| 1139 |
card.querySelector('.local-retry')?.addEventListener('click',async()=>{await j('/api/ocean/batch/'+encodeURIComponent(x.job_id)+'/retry',{method:'POST'});renderTasks()});
|
|
|
|
| 1087 |
|
| 1088 |
const TASK_OPERATION_ZH={quality_check:"数据质量检查",marine_export:"海洋数据导出",marine_query:"海洋数据查询",marine_subset:"海洋区域裁剪",fisheries_query:"渔业数据查询",task:"数据处理任务"};
|
| 1089 |
function taskOperationLabel(v){return TASK_OPERATION_ZH[String(v||"")]||String(v||"数据处理任务")}
|
| 1090 |
+
function taskStatusGroup(v){const s=String(v||"");if(/completed|done|success|成功|完成/i.test(s))return "completed";if(/failed|error|失败|异常/i.test(s))return "failed";if(/pending|todo|queued|待处理|等待/i.test(s))return "pending";return "running"}
|
| 1091 |
+
function taskStatusLabel(v){const g=taskStatusGroup(v);return g==="completed"?"已完成":g==="failed"?"失败":g==="pending"?"待处理":"处理中"}
|
| 1092 |
function taskProgressHtml(task){
|
| 1093 |
const g=taskStatusGroup(task.status),explicit=Number(task.progress);
|
| 1094 |
if(task.progress!==null&&task.progress!==undefined&&Number.isFinite(explicit))return '<div class="task-progress" title="进度 '+esc(explicit)+'%"><span style="width:'+Math.max(0,Math.min(100,explicit))+'%"></span></div><div class="row-meta">处理进度:'+esc(explicit)+'%</div>';
|
|
|
|
| 1119 |
function openAssetDetail(asset){
|
| 1120 |
document.querySelector(".drawer-backdrop")?.remove();const wrap=document.createElement("div");wrap.className="drawer-backdrop";wrap.innerHTML='<aside class="detail-drawer" role="dialog" aria-modal="true" aria-label="数据资产详情"><div class="drawer-head"><div><h2>'+esc(asset.name||"未命名资产")+'</h2><p>'+esc(taskOperationLabel(asset.operation))+' · '+esc(taskStatusLabel(asset.status))+'</p></div><button class="drawer-close" aria-label="关闭">×</button></div><div class="task-summary"><div><span>资产编号</span><b>'+esc(asset.asset_id||"-")+'</b></div><div><span>文件大小</span><b>'+esc(formatBytes(asset.size_bytes))+'</b></div><div><span>文件类型</span><b>'+esc(asset.mime_type||"-")+'</b></div><div><span>创建时间</span><b>'+esc(formatTime(asset.created_at))+'</b></div></div><div class="detail-section"><h3>资产来源</h3><div class="row-detail">来源:'+esc(asset.source||"未记录")+(asset.path?'<br>路径:'+esc(asset.path):'')+(asset.thread_id?'<br>关联会话:'+esc(asset.thread_id):'')+'</div></div><div class="card-actions">'+(asset.download_url?'<a class="download" href="'+esc(asset.download_url)+'" target="_blank" rel="noopener">↓ 下载资产</a>':'')+'<button class="view-btn primary asset-chat">在对话中分析</button></div></aside>';document.body.appendChild(wrap);const close=()=>wrap.remove();wrap.querySelector(".drawer-close").onclick=close;wrap.onclick=e=>{if(e.target===wrap)close()};wrap.querySelector(".asset-chat").onclick=()=>{close();goChat(assetContinuePrompt(asset))}
|
| 1121 |
}
|
| 1122 |
+
function openManualTaskEditor(task=null){
|
| 1123 |
+
document.querySelector(".drawer-backdrop")?.remove();
|
| 1124 |
+
const editing=!!task;
|
| 1125 |
+
const wrap=document.createElement("div");wrap.className="drawer-backdrop";
|
| 1126 |
+
wrap.innerHTML='<aside class="detail-drawer" role="dialog" aria-modal="true" aria-label="'+(editing?'编辑任务':'新建任务')+'"><div class="drawer-head"><div><h2>'+(editing?'编辑任务':'新建任务')+'</h2><p>手动任务会绑定当前账号并保存在任务中心。</p></div><button class="drawer-close" aria-label="关闭">×</button></div><div class="detail-tool"><label class="row-detail">任务名称</label><input id="manualTaskTitle" type="text" maxlength="160" placeholder="例如:跨设备同步测试" value="'+esc(task?.title||"")+'"><label class="row-detail" style="display:block;margin-top:12px">任务内容</label><textarea id="manualTaskDescription" rows="7" maxlength="4000" placeholder="写清楚要完成的事情、检查条件或后续动作。">'+esc(task?.description||"")+'</textarea><label class="row-detail" style="display:block;margin-top:12px">状态</label><select id="manualTaskStatus"><option value="pending">待处理</option><option value="running">处理中</option><option value="completed">已完成</option><option value="failed">失败</option></select></div><div class="card-actions"><button id="manualTaskSave" class="view-btn primary">'+(editing?'保存修改':'创建任务')+'</button><button class="view-btn manual-task-cancel">取消</button></div></aside>';
|
| 1127 |
+
document.body.appendChild(wrap);
|
| 1128 |
+
const close=()=>wrap.remove();
|
| 1129 |
+
wrap.querySelector(".drawer-close").onclick=close;
|
| 1130 |
+
wrap.querySelector(".manual-task-cancel").onclick=close;
|
| 1131 |
+
wrap.onclick=e=>{if(e.target===wrap)close()};
|
| 1132 |
+
const status=wrap.querySelector("#manualTaskStatus");status.value=task?.status||"pending";
|
| 1133 |
+
wrap.querySelector("#manualTaskSave").onclick=async()=>{
|
| 1134 |
+
const title=wrap.querySelector("#manualTaskTitle").value.trim();
|
| 1135 |
+
const description=wrap.querySelector("#manualTaskDescription").value.trim();
|
| 1136 |
+
if(!title){toast("请填写任务名称");wrap.querySelector("#manualTaskTitle").focus();return}
|
| 1137 |
+
const btn=wrap.querySelector("#manualTaskSave");btn.disabled=true;
|
| 1138 |
+
try{
|
| 1139 |
+
if(editing){
|
| 1140 |
+
await j("/api/sidebar/tasks/manual/"+encodeURIComponent(task.task_id),{method:"PATCH",body:JSON.stringify({user_id:USER_ID,title,description,status:status.value})});
|
| 1141 |
+
toast("任务已更新");
|
| 1142 |
+
}else{
|
| 1143 |
+
await j("/api/sidebar/tasks/manual",{method:"POST",body:JSON.stringify({user_id:USER_ID,title,description,status:status.value})});
|
| 1144 |
+
toast("任务已创建");
|
| 1145 |
+
}
|
| 1146 |
+
close();await renderTasks();
|
| 1147 |
+
}catch(err){toast("保存失败:"+(err.message||String(err)))}
|
| 1148 |
+
finally{btn.disabled=false}
|
| 1149 |
+
};
|
| 1150 |
+
setTimeout(()=>wrap.querySelector("#manualTaskTitle")?.focus(),0);
|
| 1151 |
+
}
|
| 1152 |
+
|
| 1153 |
+
async function deleteManualTask(task){
|
| 1154 |
+
if(!confirm('确认删除任务“'+(task.title||"未命名任务")+'”?'))return;
|
| 1155 |
+
try{
|
| 1156 |
+
await j("/api/sidebar/tasks/manual/"+encodeURIComponent(task.task_id)+"?user_id="+encodeURIComponent(USER_ID),{method:"DELETE"});
|
| 1157 |
+
toast("任务已删除");await renderTasks();
|
| 1158 |
+
}catch(err){toast("删除失败:"+(err.message||String(err)))}
|
| 1159 |
+
}
|
| 1160 |
+
|
| 1161 |
async function renderTasks(){
|
| 1162 |
+
const root=$("workspaceInner");root.innerHTML='<div class="view-head"><div><h1>我的任务</h1><p>查看当前登录用户的数据处理任务和生成的数据资产。</p></div><div class="card-actions"><button id="newManualTask" class="view-btn primary">+ 新建任务</button><button id="refreshTasks" class="view-btn">↻ 刷新</button></div></div><div class="loading">正在读取任务记录…</div>';$("refreshTasks").onclick=renderTasks;$("newManualTask").onclick=()=>openManualTaskEditor();
|
| 1163 |
try{
|
| 1164 |
+
const data=await j("/api/sidebar/tasks?user_id="+encodeURIComponent(USER_ID)),tasks=data.tasks||[],assets=data.assets||[],localTasks=data.local_tasks||[],allStatus=[...tasks,...localTasks],pending=allStatus.filter(x=>taskStatusGroup(x.status)==="pending").length,running=allStatus.filter(x=>taskStatusGroup(x.status)==="running").length,failed=allStatus.filter(x=>taskStatusGroup(x.status)==="failed").length,completed=allStatus.filter(x=>taskStatusGroup(x.status)==="completed").length;
|
| 1165 |
+
let html='<div class="view-head"><div><h1>我的任务</h1><p>'+esc(data.enabled?"任务、结果和数据资产已按当前用户关联":"任务记录服务尚未启用")+'</p></div><div class="card-actions"><button id="newManualTask" class="view-btn primary">+ 新建任务</button><button id="refreshTasks" class="view-btn">↻ 刷新</button></div></div><div class="summary-grid"><div class="summary-card"><strong>'+(tasks.length+localTasks.length)+'</strong><span>全部任务</span></div><div class="summary-card"><strong>'+pending+'</strong><span>待处理</span></div><div class="summary-card"><strong>'+running+'</strong><span>处理中</span></div><div class="summary-card"><strong>'+completed+'</strong><span>已完成</span></div><div class="summary-card"><strong>'+failed+'</strong><span>失败</span></div><div class="summary-card"><strong>'+assets.length+'</strong><span>数据资产</span></div></div><div class="detail-note">手动任务存储:'+esc(data.manual_task_storage==="persistent"?"账号服务器持久化(可跨设备)":"服务器会话存储(未挂��持久卷)")+'。</div>';
|
| 1166 |
if(data.error)html+='<div class="service-row"><div class="row-detail">'+esc(data.error)+'</div></div>';
|
| 1167 |
html+='<h3 style="font-size:14px;margin:20px 0 10px">持久化任务</h3><div id="persistentTaskList"></div><div class="filter-bar"><input id="taskSearch" type="search" placeholder="搜索任务类型、编号、来源或关联文件"><select id="taskStatus"><option value="all">全部状态</option><option value="running">处理中</option><option value="completed">已完成</option><option value="failed">失败</option></select><span id="taskCount" class="filter-count"></span></div><h3 style="font-size:14px;margin:20px 0 10px">处理任务</h3><div id="taskList"></div><div class="filter-bar" style="margin-top:20px"><input id="assetSearch" type="search" placeholder="搜索资产名称、类型或来源"><span id="assetCount" class="filter-count"></span></div><h3 style="font-size:14px;margin:12px 0 10px">数据资产</h3><div id="assetList"></div>';
|
| 1168 |
+
root.innerHTML=html;$("refreshTasks").onclick=renderTasks;$("newManualTask").onclick=()=>openManualTaskEditor();
|
| 1169 |
const drawPersistent=()=>{
|
| 1170 |
const box=$("persistentTaskList");if(!box)return;
|
| 1171 |
if(!localTasks.length){box.innerHTML='<div class="empty-view"><b>暂无持久化任务</b>Ocean 批量导出和大文件下载任务会保存在这里。</div>';return}
|
| 1172 |
box.innerHTML=localTasks.map((x,i)=>{
|
| 1173 |
+
if(x.kind==="manual"){const g=taskStatusGroup(x.status);return '<div class="task-row" data-manual-index="'+i+'"><div class="task-head"><b>'+esc(x.title||"手动任务")+'</b><span class="badge '+(g==="completed"?"ok":g==="failed"?"bad":"warn")+'">'+esc(taskStatusLabel(x.status))+'</span></div><div class="row-meta">'+esc(x.task_id||"")+' · 手动创建 · '+esc(formatTime(x.updated_at||x.created_at))+'</div><div class="row-detail">'+esc(x.description||"暂无任务说明")+'</div><div class="card-actions"><button class="view-btn primary manual-chat">在对话中继续</button><button class="view-btn manual-edit">编辑</button>'+(x.status!=="completed"?'<button class="view-btn manual-complete">标记完成</button>':'<button class="view-btn manual-reopen">重新打开</button>')+'<button class="view-btn danger manual-delete">删除</button></div></div>'}
|
| 1174 |
if(x.kind==="ocean_batch"){const c=x.counts||{},pct=Number(x.progress_percent||0);return '<div class="task-row" data-local-index="'+i+'"><div class="task-head"><b>Ocean 批量导出</b><span class="badge '+(x.status==="completed"?"ok":x.status==="partial"?"bad":"warn")+'">'+esc(x.status||"-")+'</span></div><div class="row-meta">'+esc(x.job_id||"")+' · '+esc(formatTime(x.updated_at||x.created_at))+(x.thread_id?' · 关联会话 '+esc(x.thread_id):'')+'</div><div class="row-detail">'+esc(x.prompt||"")+'</div><div class="ocean-batch-track"><span style="width:'+Math.min(100,pct)+'%"></span></div><div class="row-meta">'+pct.toFixed(0)+'% · 完成 '+esc(c.completed||0)+' / '+esc(x.total||0)+' · 失败 '+esc(c.failed||0)+'</div><div class="card-actions">'+(x.status==="paused"?'<button class="view-btn local-resume">继续任务</button>':'')+((c.failed||0)>0?'<button class="view-btn local-retry">仅重试失败项</button>':'')+'<button class="view-btn local-detail">查看子任务</button>'+(x.thread_id?'<button class="view-btn primary local-chat">回到关联对话</button>':'')+'</div><div class="local-subtasks" hidden></div></div>'}
|
| 1175 |
return '<div class="task-row"><div class="task-head"><b>大文件下载</b><span class="badge '+(x.status==="completed"?"ok":x.status==="failed"?"bad":"warn")+'">'+esc(x.status||"-")+'</span></div><div class="row-meta">'+esc(x.job_id||"")+' · '+esc(x.filename||"download")+' · '+esc(formatTime(x.updated_at))+'</div><div class="row-detail">已传输 '+esc(formatBytes(x.transferred_bytes||0))+(x.total_bytes?' / '+esc(formatBytes(x.total_bytes)):'')+(x.resume_supported?' · 支持断点续传':'')+'</div></div>'
|
| 1176 |
}).join('');
|
| 1177 |
+
box.querySelectorAll('[data-manual-index]').forEach(card=>{const x=localTasks[Number(card.dataset.manualIndex)];
|
| 1178 |
+
card.querySelector('.manual-edit')?.addEventListener('click',()=>openManualTaskEditor(x));
|
| 1179 |
+
card.querySelector('.manual-delete')?.addEventListener('click',()=>deleteManualTask(x));
|
| 1180 |
+
card.querySelector('.manual-chat')?.addEventListener('click',()=>goChat('请帮我继续处理这个手动任务。\n任务:'+(x.title||'')+'\n说明:'+(x.description||'')+'\n当前状态:'+taskStatusLabel(x.status)));
|
| 1181 |
+
card.querySelector('.manual-complete')?.addEventListener('click',async()=>{await j('/api/sidebar/tasks/manual/'+encodeURIComponent(x.task_id),{method:'PATCH',body:JSON.stringify({user_id:USER_ID,status:'completed'})});toast('已标记完成');renderTasks()});
|
| 1182 |
+
card.querySelector('.manual-reopen')?.addEventListener('click',async()=>{await j('/api/sidebar/tasks/manual/'+encodeURIComponent(x.task_id),{method:'PATCH',body:JSON.stringify({user_id:USER_ID,status:'pending'})});toast('任务已重新打开');renderTasks()});
|
| 1183 |
+
});
|
| 1184 |
box.querySelectorAll('[data-local-index]').forEach(card=>{const x=localTasks[Number(card.dataset.localIndex)];
|
| 1185 |
card.querySelector('.local-resume')?.addEventListener('click',async()=>{await j('/api/ocean/batch/'+encodeURIComponent(x.job_id)+'/resume',{method:'POST'});renderTasks()});
|
| 1186 |
card.querySelector('.local-retry')?.addEventListener('click',async()=>{await j('/api/ocean/batch/'+encodeURIComponent(x.job_id)+'/retry',{method:'POST'});renderTasks()});
|
tests/test_api_entry_preflight_v456.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_guard_is_before_auth_and_dispatch():
|
| 8 |
chat=(ROOT/"routes"/"chat.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_guard_is_before_auth_and_dispatch():
|
| 8 |
chat=(ROOT/"routes"/"chat.py").read_text(encoding="utf-8")
|
tests/test_chat_lifecycle_progress_v393.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_393():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_frontend_lifecycle_progress():
|
| 9 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_393():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_frontend_lifecycle_progress():
|
| 9 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
tests/test_clarification_ui_v454.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_missing_date_reuses_canonical_region_bbox():
|
| 8 |
rt=(ROOT/"services"/"chat_runtime.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_missing_date_reuses_canonical_region_bbox():
|
| 8 |
rt=(ROOT/"services"/"chat_runtime.py").read_text(encoding="utf-8")
|
tests/test_classification_cleanup_v466.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_seaaroundus_alias():
|
| 8 |
py=(ROOT/"sidebar_catalog.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_seaaroundus_alias():
|
| 8 |
py=(ROOT/"sidebar_catalog.py").read_text(encoding="utf-8")
|
tests/test_context_and_multitask_finalize_v451.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_new_query_slot_reset():
|
| 8 |
rt=(ROOT/"services"/"chat_runtime.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_new_query_slot_reset():
|
| 8 |
rt=(ROOT/"services"/"chat_runtime.py").read_text(encoding="utf-8")
|
tests/test_download_host_authorization_v460.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_batch_url_authorization_helper_exists():
|
| 8 |
py=(ROOT/"routes"/"downloads.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_batch_url_authorization_helper_exists():
|
| 8 |
py=(ROOT/"routes"/"downloads.py").read_text(encoding="utf-8")
|
tests/test_download_opfs_fallback_v459.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_opfs_is_probed_not_assumed():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_opfs_is_probed_not_assumed():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
tests/test_download_progress_v391.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_391():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_p1_label_removed():
|
| 9 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_391():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_p1_label_removed():
|
| 9 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
tests/test_download_resilience_v422.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_refresh_route_registered():
|
| 9 |
t=(ROOT/"routes"/"ocean_batch.py").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_refresh_route_registered():
|
| 9 |
t=(ROOT/"routes"/"ocean_batch.py").read_text(encoding="utf-8")
|
tests/test_frontend_preflight_v457.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_frontend_preflight_exists_before_progress():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_frontend_preflight_exists_before_progress():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
tests/test_history_sidebar_v373.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_373():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_project_history_backend():
|
| 9 |
route=(ROOT/"routes"/"project_package.py").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_373():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_project_history_backend():
|
| 9 |
route=(ROOT/"routes"/"project_package.py").read_text(encoding="utf-8")
|
tests/test_large_download_jobs_v400.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_400():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_download_job_backend():
|
| 9 |
route=(ROOT/"routes"/"downloads.py").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_400():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_download_job_backend():
|
| 9 |
route=(ROOT/"routes"/"downloads.py").read_text(encoding="utf-8")
|
tests/test_manual_tasks_v469.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
+
|
| 4 |
+
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
+
|
| 7 |
+
def test_manual_task_backend_exists():
|
| 8 |
+
service=(ROOT/"services/manual_tasks.py").read_text(encoding="utf-8")
|
| 9 |
+
routes=(ROOT/"routes/manual_tasks.py").read_text(encoding="utf-8")
|
| 10 |
+
assert "class ManualTaskStore" in service
|
| 11 |
+
assert "VALID_STATUSES" in service
|
| 12 |
+
assert "/api/sidebar/tasks/manual" in routes
|
| 13 |
+
assert 'methods=["POST"]' in routes
|
| 14 |
+
assert 'methods=["PATCH"]' in routes
|
| 15 |
+
assert 'methods=["DELETE"]' in routes
|
| 16 |
+
|
| 17 |
+
def test_ui_server_lists_manual_tasks():
|
| 18 |
+
py=(ROOT/"ui_server.py").read_text(encoding="utf-8")
|
| 19 |
+
assert "MANUAL_TASK_STORE.list_user" in py
|
| 20 |
+
assert '"kind":"manual"' in py
|
| 21 |
+
assert "register_manual_task_routes" in py
|
| 22 |
+
|
| 23 |
+
def test_frontend_new_task_controls():
|
| 24 |
+
js=(ROOT/"static/js/app.js").read_text(encoding="utf-8")
|
| 25 |
+
assert "+ 新建任务" in js
|
| 26 |
+
assert "openManualTaskEditor" in js
|
| 27 |
+
assert "标记完成" in js
|
| 28 |
+
assert "重新打开" in js
|
| 29 |
+
assert "manual-delete" in js
|
| 30 |
+
assert "账号服务器持久化(可跨设备)" in js
|
tests/test_merged_health_v433.py
CHANGED
|
@@ -3,7 +3,7 @@ from services.ocean_batch import parse_ocean_batch_request
|
|
| 3 |
ROOT=Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_merged_version():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_ocean_month_batch_survives_merge():
|
| 9 |
p=parse_ocean_batch_request("查询 2002 年 8 月西北太平洋的 SST 数据,并导出为 CSV 文件供我下载")
|
|
|
|
| 3 |
ROOT=Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_merged_version():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_ocean_month_batch_survives_merge():
|
| 9 |
p=parse_ocean_batch_request("查询 2002 年 8 月西北太平洋的 SST 数据,并导出为 CSV 文件供我下载")
|
tests/test_multitask_observability_v442.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_frontend_sends_multitask_trace_headers():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_frontend_sends_multitask_trace_headers():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
tests/test_multitask_sse_finalize_v452.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_parallel_sse_uses_real_blank_line_separator():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_parallel_sse_uses_real_blank_line_separator():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
tests/test_noaa_ramldb_roles_v465.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_noaa_and_ramldb_aliases():
|
| 8 |
py=(ROOT/"sidebar_catalog.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_noaa_and_ramldb_aliases():
|
| 8 |
py=(ROOT/"sidebar_catalog.py").read_text(encoding="utf-8")
|
tests/test_ocean_batch_downloads_v420.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_420():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_manifest_has_filename_and_sizes():
|
| 9 |
svc=(ROOT/"services"/"ocean_batch.py").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_420():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_manifest_has_filename_and_sizes():
|
| 9 |
svc=(ROOT/"services"/"ocean_batch.py").read_text(encoding="utf-8")
|
tests/test_ocean_batch_routing_v421.py
CHANGED
|
@@ -4,7 +4,7 @@ from services.ocean_batch import parse_ocean_batch_request
|
|
| 4 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 5 |
|
| 6 |
def test_version_421():
|
| 7 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 8 |
|
| 9 |
def test_exact_user_month_query_routes_to_batch():
|
| 10 |
p=parse_ocean_batch_request("查询 2002 年 8 月西北太平洋的 SST 数据,并导出为 CSV 文件供我下载")
|
|
|
|
| 4 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 5 |
|
| 6 |
def test_version_421():
|
| 7 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 8 |
|
| 9 |
def test_exact_user_month_query_routes_to_batch():
|
| 10 |
p=parse_ocean_batch_request("查询 2002 年 8 月西北太平洋的 SST 数据,并导出为 CSV 文件供我下载")
|
tests/test_ocean_batch_v410.py
CHANGED
|
@@ -4,7 +4,7 @@ from services.ocean_batch import parse_ocean_batch_request
|
|
| 4 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 5 |
|
| 6 |
def test_version_410():
|
| 7 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 8 |
|
| 9 |
def test_month_three_variable_batch_plan():
|
| 10 |
p=parse_ocean_batch_request("查询并导出 2002 年 8 月西北太平洋 120°E–140°E、20°N–40°N 的 SST、SSH、CHL 数据,并打包下载")
|
|
|
|
| 4 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 5 |
|
| 6 |
def test_version_410():
|
| 7 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 8 |
|
| 9 |
def test_month_three_variable_batch_plan():
|
| 10 |
p=parse_ocean_batch_request("查询并导出 2002 年 8 月西北太平洋 120°E–140°E、20°N–40°N 的 SST、SSH、CHL 数据,并打包下载")
|
tests/test_ocean_date_default_v453.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_missing_date_never_defaults_to_today():
|
| 8 |
rt=(ROOT/"services"/"chat_runtime.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_missing_date_never_defaults_to_today():
|
| 8 |
rt=(ROOT/"services"/"chat_runtime.py").read_text(encoding="utf-8")
|
tests/test_ocean_download_url_base_v462.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_generic_public_base_not_used():
|
| 8 |
py=(ROOT/"services"/"ocean_batch.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_generic_public_base_not_used():
|
| 8 |
py=(ROOT/"services"/"ocean_batch.py").read_text(encoding="utf-8")
|
tests/test_p0_finish_v380.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib, re, subprocess, shutil
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_380():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_chat_runtime_extracted():
|
| 9 |
ui=(ROOT/"ui_server.py").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_380():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_chat_runtime_extracted():
|
| 9 |
ui=(ROOT/"ui_server.py").read_text(encoding="utf-8")
|
tests/test_p0_phase2_v371.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_frontend_feature_modules_exist():
|
| 9 |
html=(ROOT/"app.html").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_frontend_feature_modules_exist():
|
| 9 |
html=(ROOT/"app.html").read_text(encoding="utf-8")
|
tests/test_p0_phase3_v372.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_372():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_route_modules_exist():
|
| 9 |
for name in ("datasets.py","user_state.py","project_package.py","data_health.py"):
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_372():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_route_modules_exist():
|
| 9 |
for name in ("datasets.py","user_state.py","project_package.py","data_health.py"):
|
tests/test_platform_reliability_v450.py
CHANGED
|
@@ -6,7 +6,7 @@ from services.ocean_batch import parse_ocean_batch_request
|
|
| 6 |
ROOT=Path(__file__).resolve().parents[1]
|
| 7 |
|
| 8 |
def test_version():
|
| 9 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 10 |
|
| 11 |
def test_squid_center_uses_repository_membership():
|
| 12 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
|
|
|
| 6 |
ROOT=Path(__file__).resolve().parents[1]
|
| 7 |
|
| 8 |
def test_version():
|
| 9 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 10 |
|
| 11 |
def test_squid_center_uses_repository_membership():
|
| 12 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
tests/test_quality_center_v362.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_371():
|
| 6 |
-
assert (ROOT / "VERSION").read_text(encoding="utf-8").strip() == "4.6.
|
| 7 |
|
| 8 |
def test_native_health_backend():
|
| 9 |
text = (ROOT / "routes" / "data_health.py").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_371():
|
| 6 |
+
assert (ROOT / "VERSION").read_text(encoding="utf-8").strip() == "4.6.9"
|
| 7 |
|
| 8 |
def test_native_health_backend():
|
| 9 |
text = (ROOT / "routes" / "data_health.py").read_text(encoding="utf-8")
|
tests/test_quality_trust_v361.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version_current():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_quality_confidence_is_now_native_api_metric():
|
| 8 |
text=(ROOT/"routes"/"data_health.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version_current():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_quality_confidence_is_now_native_api_metric():
|
| 8 |
text=(ROOT/"routes"/"data_health.py").read_text(encoding="utf-8")
|
tests/test_reference_cleanup_v468.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_reference_and_reproducibility_are_auxiliary():
|
| 8 |
py=(ROOT/"routes"/"datasets.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_reference_and_reproducibility_are_auxiliary():
|
| 8 |
py=(ROOT/"routes"/"datasets.py").read_text(encoding="utf-8")
|
tests/test_release_stability.py
CHANGED
|
@@ -7,7 +7,7 @@ def test_version_is_single_source():
|
|
| 7 |
version = (ROOT / "VERSION").read_text(encoding="utf-8").strip()
|
| 8 |
ui = (ROOT / "ui_server.py").read_text(encoding="utf-8")
|
| 9 |
html = (ROOT / "app.html").read_text(encoding="utf-8")
|
| 10 |
-
assert version == "4.6.
|
| 11 |
assert 'with_name("VERSION")' in ui
|
| 12 |
assert "__APP_VERSION__" in html
|
| 13 |
|
|
|
|
| 7 |
version = (ROOT / "VERSION").read_text(encoding="utf-8").strip()
|
| 8 |
ui = (ROOT / "ui_server.py").read_text(encoding="utf-8")
|
| 9 |
html = (ROOT / "app.html").read_text(encoding="utf-8")
|
| 10 |
+
assert version == "4.6.9"
|
| 11 |
assert 'with_name("VERSION")' in ui
|
| 12 |
assert "__APP_VERSION__" in html
|
| 13 |
|
tests/test_resumable_batch_download_v458.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_network_loss_pauses_not_fails_all():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_network_loss_pauses_not_fails_all():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
tests/test_route_region_clarification_v455.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_route_guard_exists_before_agent_dispatch():
|
| 8 |
chat=(ROOT/"routes"/"chat.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_route_guard_exists_before_agent_dispatch():
|
| 8 |
chat=(ROOT/"routes"/"chat.py").read_text(encoding="utf-8")
|
tests/test_runtime_hotfix_v376.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_376():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_no_stray_async_before_sidebar_toggle():
|
| 9 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_376():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_no_stray_async_before_sidebar_toggle():
|
| 9 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
tests/test_sidebar_collapse_v374.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_374():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_sidebar_toggle_markup():
|
| 9 |
html=(ROOT/"app.html").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_374():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_sidebar_toggle_markup():
|
| 9 |
html=(ROOT/"app.html").read_text(encoding="utf-8")
|
tests/test_sidebar_partial_collapse_v375.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_375():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_sidebar_order_and_groups():
|
| 9 |
html=(ROOT/"app.html").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_375():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_sidebar_order_and_groups():
|
| 9 |
html=(ROOT/"app.html").read_text(encoding="utf-8")
|
tests/test_smart_progress_avatar_sync_v441.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_simple_chat_hides_progress_until_needed():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_simple_chat_hides_progress_until_needed():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
tests/test_source_mapping_roles_v464.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_new_source_mappings():
|
| 8 |
py=(ROOT/"sidebar_catalog.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_new_source_mappings():
|
| 8 |
py=(ROOT/"sidebar_catalog.py").read_text(encoding="utf-8")
|
tests/test_squid_finalize_v467.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_ccamlr_and_ommastrephidae_mappings():
|
| 8 |
py=(ROOT/"sidebar_catalog.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_ccamlr_and_ommastrephidae_mappings():
|
| 8 |
py=(ROOT/"sidebar_catalog.py").read_text(encoding="utf-8")
|
tests/test_sse_heartbeat_v401.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_401():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_chat_has_independent_heartbeat_wrapper():
|
| 9 |
route=(ROOT/"routes"/"chat.py").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_401():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_chat_has_independent_heartbeat_wrapper():
|
| 9 |
route=(ROOT/"routes"/"chat.py").read_text(encoding="utf-8")
|
tests/test_ui_avatar_multitask_v440.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_assistant_avatar_removed_user_avatar_customizable():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_assistant_avatar_removed_user_avatar_customizable():
|
| 8 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
tests/test_unclassified_inventory_v463.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_backend_unique_classification_and_route():
|
| 8 |
py=(ROOT/"routes"/"datasets.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_backend_unique_classification_and_route():
|
| 8 |
py=(ROOT/"routes"/"datasets.py").read_text(encoding="utf-8")
|
tests/test_unified_download_manager_v392.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_392():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_download_proxy_route_registered():
|
| 9 |
route=(ROOT/"routes"/"downloads.py").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_392():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_download_proxy_route_registered():
|
| 9 |
route=(ROOT/"routes"/"downloads.py").read_text(encoding="utf-8")
|
tests/test_unified_query_v390.py
CHANGED
|
@@ -4,7 +4,7 @@ from services.unified_query import plan_unified_query
|
|
| 4 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 5 |
|
| 6 |
def test_version_390():
|
| 7 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 8 |
|
| 9 |
def test_joint_squid_environment_plan():
|
| 10 |
p=plan_unified_query("查询 2002 年 8 月西北太平洋柔鱼记录,并匹配 SST、SSH、CHL")
|
|
|
|
| 4 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 5 |
|
| 6 |
def test_version_390():
|
| 7 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 8 |
|
| 9 |
def test_joint_squid_environment_plan():
|
| 10 |
p=plan_unified_query("查询 2002 年 8 月西北太平洋柔鱼记录,并匹配 SST、SSH、CHL")
|
tests/test_upstream_refresh_retry_v461.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 6 |
|
| 7 |
def test_probe_returns_status_and_rejects_dead_url():
|
| 8 |
py=(ROOT/"routes"/"downloads.py").read_text(encoding="utf-8")
|
|
|
|
| 2 |
ROOT=Path(__file__).resolve().parents[1]
|
| 3 |
|
| 4 |
def test_version():
|
| 5 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 6 |
|
| 7 |
def test_probe_returns_status_and_rejects_dead_url():
|
| 8 |
py=(ROOT/"routes"/"downloads.py").read_text(encoding="utf-8")
|
tests/test_v381_navigation_ocean_intent.py
CHANGED
|
@@ -3,7 +3,7 @@ import pathlib, ast
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_381():
|
| 6 |
-
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.
|
| 7 |
|
| 8 |
def test_recent_conversation_switches_to_chat():
|
| 9 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|
|
|
|
| 3 |
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
| 4 |
|
| 5 |
def test_version_381():
|
| 6 |
+
assert (ROOT/"VERSION").read_text(encoding="utf-8").strip()=="4.6.9"
|
| 7 |
|
| 8 |
def test_recent_conversation_switches_to_chat():
|
| 9 |
js=(ROOT/"static"/"js"/"app.js").read_text(encoding="utf-8")
|