Files
BlueArchiveToolkit/infrastructure/src/bin/bat/translation_query.rs
T
2026-08-03 00:43:11 +08:00

233 lines
9.4 KiB
Rust

use super::*;
pub(super) fn build_translation_tasks_report(
state_dir: &Path,
query: OfficialTextUnitTaskQuery,
offset: usize,
limit: usize,
) -> anyhow::Result<serde_json::Value> {
let (_, version_state) = read_daemon_resource_state(state_dir)?;
let current = version_state
.as_ref()
.and_then(|state| state.current_completed_version.as_ref());
let Some(record) = current else {
return Ok(serde_json::json!({ "available": false }));
};
let task_queue_path = record
.resource_root
.join(bat_infrastructure::OFFICIAL_TEXTUNIT_TASK_QUEUE_FILE);
let task_repository_path =
SqliteTranslationTaskRepository::repository_path(&record.resource_root);
let Some(queue) = bat_infrastructure::read_textunit_task_queue_at(&record.resource_root)
.map_err(anyhow::Error::msg)?
else {
return Ok(serde_json::json!({
"available": false,
"current_version_id": record.id,
"resource_root": record.resource_root,
"textunit_task_queue_path": task_queue_path,
"task_repository_path": task_repository_path,
"task_repository_available": false,
}));
};
let task_repository_available =
sqlite_file_exists_no_symlink(&task_repository_path, "翻译任务状态数据库")?;
let (total_entries, entries) = if task_repository_available {
query_translation_task_repository(&task_repository_path, &query, offset, limit)?
} else {
let mut queue_query = query.clone();
queue_query.task_status = None;
queue_query.has_failure_reason = None;
let matches = bat_infrastructure::query_textunit_tasks(&queue, &queue_query);
let persisted = matches
.into_iter()
.cloned()
.map(|task| {
bat_infrastructure::PersistedTranslationTask::from_queued_task(
task,
queue.generated_unix_seconds,
)
})
.filter(|task| {
query
.task_status
.as_ref()
.is_none_or(|status| task.task_status.as_str() == status)
})
.filter(|task| {
query
.has_failure_reason
.is_none_or(|has_reason| task.failure_reason.is_some() == has_reason)
})
.collect::<Vec<_>>();
let total_entries = persisted.len();
let entries = persisted
.into_iter()
.skip(offset)
.take(limit)
.collect::<Vec<_>>();
(total_entries as u64, entries)
};
Ok(serde_json::json!({
"available": true,
"current_version_id": record.id,
"resource_root": record.resource_root,
"textunit_task_queue_path": task_queue_path,
"task_repository_path": task_repository_path,
"task_repository_available": task_repository_available,
"task_repository_schema_version": bat_infrastructure::TRANSLATION_TASK_SCHEMA_VERSION,
"summary": queue.summary,
"total_entries": total_entries,
"offset": offset,
"limit": limit,
"query": translation_task_query_json(&query),
"entries": entries,
}))
}
pub(super) fn build_translation_handoff_report(
state_dir: &Path,
) -> anyhow::Result<serde_json::Value> {
let (_, version_state) = read_daemon_resource_state(state_dir)?;
let current = version_state
.as_ref()
.and_then(|state| state.current_completed_version.as_ref());
let Some(record) = current else {
return Ok(serde_json::json!({ "available": false }));
};
let resource_root = &record.resource_root;
let task_queue_path = resource_root.join(bat_infrastructure::OFFICIAL_TEXTUNIT_TASK_QUEUE_FILE);
let handoff_path = resource_root.join(bat_infrastructure::TRANSLATION_HANDOFF_FILE);
let repository_path = SqliteTranslationTaskRepository::repository_path(resource_root);
let Some(queue) = bat_infrastructure::read_textunit_task_queue_at(resource_root)
.map_err(anyhow::Error::msg)?
else {
return Ok(serde_json::json!({
"available": false,
"current_version_id": record.id,
"resource_root": resource_root,
"textunit_task_queue_path": task_queue_path,
"translation_handoff_path": handoff_path,
"task_repository_path": repository_path,
}));
};
let task_repository_available =
sqlite_file_exists_no_symlink(&repository_path, "翻译任务状态数据库")?;
let tasks = if task_repository_available {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
runtime.block_on(async {
let repository = SqliteTranslationTaskRepository::open(&repository_path)
.await
.map_err(|error| anyhow::anyhow!("{error}"))?;
repository
.list(&OfficialTextUnitTaskQuery::default())
.await
.map_err(|error| anyhow::anyhow!("{error}"))
})?
} else {
queue
.tasks
.iter()
.cloned()
.map(|task| {
bat_infrastructure::PersistedTranslationTask::from_queued_task(
task,
queue.generated_unix_seconds,
)
})
.collect::<Vec<_>>()
};
let handoff = bat_infrastructure::build_translation_handoff(&queue, &tasks);
let handoff_file_available = sqlite_file_exists_no_symlink(&handoff_path, "翻译 handoff")?;
Ok(serde_json::json!({
"available": true,
"current_version_id": record.id,
"resource_root": resource_root,
"textunit_task_queue_path": task_queue_path,
"translation_handoff_path": handoff_path,
"translation_handoff_file_available": handoff_file_available,
"task_repository_path": repository_path,
"task_repository_available": task_repository_available,
"task_repository_schema_version": bat_infrastructure::TRANSLATION_TASK_SCHEMA_VERSION,
"handoff_schema_version": bat_infrastructure::TRANSLATION_HANDOFF_SCHEMA_VERSION,
"handoff": handoff,
}))
}
pub(super) fn update_translation_task_status_report(
state_dir: &Path,
params: Option<&serde_json::Value>,
) -> anyhow::Result<serde_json::Value> {
let task_id = rpc_string_param(params, "task_id")
.ok_or_else(|| anyhow::anyhow!("translation.task.update 缺少 task_id"))?;
let status_label = rpc_string_param(params, "status")
.ok_or_else(|| anyhow::anyhow!("translation.task.update 缺少 status"))?;
let status = TranslationTaskStatus::parse(status_label)
.ok_or_else(|| anyhow::anyhow!("不支持的翻译任务 worker 状态:{status_label}"))?;
let failure_reason = rpc_string_param(params, "failure_reason")
.or_else(|| rpc_string_param(params, "reason"))
.map(str::to_string);
let provider_run_id = rpc_string_param(params, "provider_run_id").map(str::to_string);
let (_, version_state) = read_daemon_resource_state(state_dir)?;
let current = version_state
.as_ref()
.and_then(|state| state.current_completed_version.as_ref())
.ok_or_else(|| anyhow::anyhow!("没有可更新翻译任务的当前官方 release"))?;
let repository_path = SqliteTranslationTaskRepository::repository_path(&current.resource_root);
if !sqlite_file_exists_no_symlink(&repository_path, "翻译任务状态数据库")? {
return Err(anyhow::anyhow!(
"翻译任务状态数据库不存在:{}",
repository_path.display()
));
}
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
let task = runtime.block_on(async {
let repository = SqliteTranslationTaskRepository::open(&repository_path)
.await
.map_err(|error| anyhow::anyhow!("{error}"))?;
repository
.update_status(task_id, status, failure_reason, provider_run_id)
.await
.map_err(|error| anyhow::anyhow!("{error}"))
})?;
Ok(serde_json::json!({
"available": true,
"current_version_id": current.id,
"task_repository_path": repository_path,
"entry": task,
}))
}
pub(super) fn textunit_query_json(query: &OfficialTextUnitQuery) -> serde_json::Value {
serde_json::json!({
"destination": query.destination.clone(),
"path_pattern": query.path_pattern.clone(),
"archive_entry": query.archive_entry.clone(),
"path_id": query.path_id,
"class_id": query.class_id,
"field_path": query.field_path.clone(),
"format": query.format.clone(),
})
}
pub(super) fn translation_task_query_json(query: &OfficialTextUnitTaskQuery) -> serde_json::Value {
serde_json::json!({
"task_id": query.task_id.clone(),
"official_release_id": query.official_release_id.clone(),
"destination": query.destination.clone(),
"path_pattern": query.path_pattern.clone(),
"archive_entry": query.archive_entry.clone(),
"status": query.status.clone(),
"task_status": query.task_status.clone(),
"parse_status": query.parse_status.clone(),
"text_unit_format": query.text_unit_format.clone(),
"has_reason": query.has_reason,
"has_failure_reason": query.has_failure_reason,
})
}