2ND
This commit is contained in:
172
app/api/files.py
172
app/api/files.py
@@ -9,6 +9,8 @@ Modified: 2024-01-28
|
||||
"""
|
||||
|
||||
import json
|
||||
import zipfile
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from flask import Blueprint, request, jsonify, send_file, current_app, g
|
||||
from werkzeug.utils import secure_filename
|
||||
@@ -122,9 +124,36 @@ def upload_file():
|
||||
|
||||
logger.info(f"File uploaded successfully: {job.job_uuid} - {file_info['filename']}")
|
||||
|
||||
# 觸發翻譯任務(這裡會在實作 Celery 時加入)
|
||||
# from app.tasks.translation import process_translation_job
|
||||
# process_translation_job.delay(job.id)
|
||||
# 觸發翻譯任務
|
||||
try:
|
||||
from app.tasks.translation import process_translation_job
|
||||
|
||||
# 嘗試使用 Celery 異步處理
|
||||
try:
|
||||
task = process_translation_job.delay(job.id)
|
||||
logger.info(f"Translation task queued with Celery: {task.id} for job {job.job_uuid}")
|
||||
except Exception as celery_error:
|
||||
logger.warning(f"Celery not available, falling back to synchronous processing: {str(celery_error)}")
|
||||
|
||||
# Celery 不可用時,使用同步處理
|
||||
try:
|
||||
from app.services.translation_service import TranslationService
|
||||
service = TranslationService()
|
||||
|
||||
# 在後台執行翻譯(同步處理)
|
||||
logger.info(f"Starting synchronous translation for job {job.job_uuid}")
|
||||
result = service.translate_document(job.job_uuid)
|
||||
logger.info(f"Synchronous translation completed for job {job.job_uuid}: {result}")
|
||||
|
||||
except Exception as sync_error:
|
||||
logger.error(f"Synchronous translation failed for job {job.job_uuid}: {str(sync_error)}")
|
||||
job.update_status('FAILED', error_message=f"翻譯處理失敗: {str(sync_error)}")
|
||||
db.session.commit()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to process translation for job {job.job_uuid}: {str(e)}")
|
||||
job.update_status('FAILED', error_message=f"任務處理失敗: {str(e)}")
|
||||
db.session.commit()
|
||||
|
||||
return jsonify(create_response(
|
||||
success=True,
|
||||
@@ -440,4 +469,141 @@ def get_supported_languages():
|
||||
success=False,
|
||||
error='SYSTEM_ERROR',
|
||||
message='取得支援語言失敗'
|
||||
)), 500
|
||||
|
||||
|
||||
@files_bp.route('/<job_uuid>/download/batch', methods=['GET'])
|
||||
@jwt_login_required
|
||||
def download_batch_files(job_uuid):
|
||||
"""批量下載所有翻譯檔案為 ZIP"""
|
||||
try:
|
||||
# 驗證 UUID 格式
|
||||
validate_job_uuid(job_uuid)
|
||||
|
||||
# 取得任務
|
||||
job = TranslationJob.query.filter_by(job_uuid=job_uuid).first()
|
||||
|
||||
if not job:
|
||||
return jsonify(create_response(
|
||||
success=False,
|
||||
error='JOB_NOT_FOUND',
|
||||
message='任務不存在'
|
||||
)), 404
|
||||
|
||||
# 檢查權限
|
||||
if job.user_id != g.current_user_id and not g.is_admin:
|
||||
return jsonify(create_response(
|
||||
success=False,
|
||||
error='PERMISSION_DENIED',
|
||||
message='無權限存取此檔案'
|
||||
)), 403
|
||||
|
||||
# 檢查任務狀態
|
||||
if job.status != 'COMPLETED':
|
||||
return jsonify(create_response(
|
||||
success=False,
|
||||
error='JOB_NOT_COMPLETED',
|
||||
message='任務尚未完成'
|
||||
)), 400
|
||||
|
||||
# 收集所有翻譯檔案
|
||||
translated_files = job.get_translated_files()
|
||||
|
||||
if not translated_files:
|
||||
return jsonify(create_response(
|
||||
success=False,
|
||||
error='NO_TRANSLATED_FILES',
|
||||
message='沒有找到翻譯檔案'
|
||||
)), 404
|
||||
|
||||
# 建立臨時 ZIP 檔案
|
||||
temp_dir = tempfile.gettempdir()
|
||||
zip_filename = f"{job.original_filename.split('.')[0]}_translations_{job.job_uuid[:8]}.zip"
|
||||
zip_path = Path(temp_dir) / zip_filename
|
||||
|
||||
try:
|
||||
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
||||
files_added = 0
|
||||
|
||||
# 添加原始檔案
|
||||
original_file = job.get_original_file()
|
||||
if original_file and Path(original_file.file_path).exists():
|
||||
zip_file.write(
|
||||
original_file.file_path,
|
||||
f"original/{original_file.filename}"
|
||||
)
|
||||
files_added += 1
|
||||
|
||||
# 添加所有翻譯檔案(避免重複)
|
||||
added_files = set() # 追蹤已添加的檔案,避免重複
|
||||
for tf in translated_files:
|
||||
file_path = Path(tf.file_path)
|
||||
if file_path.exists():
|
||||
# 按語言建立資料夾結構
|
||||
archive_name = f"{tf.language_code}/{tf.filename}"
|
||||
|
||||
# 檢查是否已經添加過這個檔案
|
||||
if archive_name not in added_files:
|
||||
zip_file.write(str(file_path), archive_name)
|
||||
added_files.add(archive_name)
|
||||
files_added += 1
|
||||
else:
|
||||
logger.warning(f"Translation file not found: {tf.file_path}")
|
||||
|
||||
if files_added == 0:
|
||||
return jsonify(create_response(
|
||||
success=False,
|
||||
error='NO_FILES_TO_ZIP',
|
||||
message='沒有可用的檔案進行壓縮'
|
||||
)), 404
|
||||
|
||||
# 檢查 ZIP 檔案是否建立成功
|
||||
if not zip_path.exists():
|
||||
return jsonify(create_response(
|
||||
success=False,
|
||||
error='ZIP_CREATION_FAILED',
|
||||
message='ZIP 檔案建立失敗'
|
||||
)), 500
|
||||
|
||||
# 記錄下載日誌
|
||||
SystemLog.info(
|
||||
'files.download_batch',
|
||||
f'Batch files downloaded: {zip_filename}',
|
||||
user_id=g.current_user_id,
|
||||
job_id=job.id,
|
||||
extra_data={
|
||||
'zip_filename': zip_filename,
|
||||
'files_count': files_added,
|
||||
'job_uuid': job_uuid
|
||||
}
|
||||
)
|
||||
|
||||
logger.info(f"Batch files downloaded: {job.job_uuid} - {files_added} files in ZIP")
|
||||
|
||||
# 發送 ZIP 檔案
|
||||
return send_file(
|
||||
str(zip_path),
|
||||
as_attachment=True,
|
||||
download_name=zip_filename,
|
||||
mimetype='application/zip'
|
||||
)
|
||||
|
||||
finally:
|
||||
# 清理臨時檔案(在發送後會自動清理)
|
||||
pass
|
||||
|
||||
except ValidationError as e:
|
||||
return jsonify(create_response(
|
||||
success=False,
|
||||
error=e.error_code,
|
||||
message=str(e)
|
||||
)), 400
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Batch download error: {str(e)}")
|
||||
|
||||
return jsonify(create_response(
|
||||
success=False,
|
||||
error='SYSTEM_ERROR',
|
||||
message='批量下載失敗'
|
||||
)), 500
|
Reference in New Issue
Block a user