first commit
This commit is contained in:
3
router/__init__.py
Normal file
3
router/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .ocr_api_router import router as ocr_api_router
|
||||
|
||||
__all__ = ["ocr_api_router"]
|
||||
127
router/ocr_api_router.py
Normal file
127
router/ocr_api_router.py
Normal file
@@ -0,0 +1,127 @@
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from celery.result import AsyncResult
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from tasks import (
|
||||
celery_app,
|
||||
run_ocr_pipeline, # 🔁 새로 만든 체인 함수 임포트
|
||||
)
|
||||
from utils.checking_keys import create_key
|
||||
from utils.redis_utils import get_redis_client
|
||||
|
||||
router = APIRouter(prefix="/ocr", tags=["OCR"])
|
||||
|
||||
redis_client = get_redis_client()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@router.post("", summary="🔍 presigned URL 기반 비동기 OCR 처리")
|
||||
async def ocr_endpoint(file_requests: dict):
|
||||
"""
|
||||
Presigned URL과 OCR 모델을 지정하여 비동기 OCR 작업을 요청합니다.
|
||||
|
||||
- **`file_url`**: OCR을 수행할 파일에 접근할 수 있는 Presigned URL
|
||||
- **`filename`**: 원본 파일의 이름
|
||||
- **`ocr_model`**: 사용할 OCR 모델 (`tesseract`, `pp-ocr`, `pp-structure`, `upstage` 중 선택)
|
||||
|
||||
요청이 접수되면, 작업 추적을 위한 `request_id`와 `task_id`가 즉시 반환됩니다.
|
||||
"""
|
||||
results = []
|
||||
file_url = file_requests.get("file_url")
|
||||
filename = file_requests.get("filename")
|
||||
ocr_model = file_requests.get("ocr_model")
|
||||
|
||||
if not file_url or not filename:
|
||||
raise HTTPException(status_code=400, detail="file_url, filename 필수")
|
||||
|
||||
request_id = create_key()
|
||||
task_id = create_key()
|
||||
run_ocr_pipeline(file_url, filename, request_id, task_id, ocr_model)
|
||||
|
||||
# Redis에 request_id → task_id 매핑 저장
|
||||
try:
|
||||
redis_client.hset("ocr_task_mapping", request_id, task_id)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"작업 정보 저장 오류: {str(e)}")
|
||||
|
||||
# 작업 로그 redis에 기록
|
||||
try:
|
||||
log_entry = {
|
||||
"status": "작업 접수",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"task_id": task_id,
|
||||
"initial_file": filename,
|
||||
}
|
||||
redis_client.rpush(f"ocr_status:{request_id}", json.dumps(log_entry))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 작업을 등록한 후, 실제 OCR 처리를 기다리지 않고 즉시 응답
|
||||
results.append(
|
||||
{
|
||||
"message": "OCR 작업이 접수되었습니다.",
|
||||
"request_id": request_id,
|
||||
"task_id": task_id,
|
||||
"status_check_url": f"/ocr/progress/{request_id}",
|
||||
}
|
||||
)
|
||||
|
||||
return JSONResponse(content={"results": results})
|
||||
|
||||
|
||||
# 실제 OCR 결과는 GET /ocr/progress/{request_id} 엔드포인트를 통해 별도로 조회
|
||||
@router.get("/progress/{request_id}", summary="📊 OCR 진행 상태 및 결과 조회")
|
||||
async def check_progress(request_id: str):
|
||||
"""
|
||||
`request_id`를 이용해 OCR 작업의 진행 상태와 최종 결과를 조회합니다.
|
||||
|
||||
- **`celery_status`**: Celery 작업의 현재 상태 (`PENDING`, `STARTED`, `SUCCESS`, `FAILURE` 등)
|
||||
- **`progress_logs`**: 작업 접수부터 완료까지의 단계별 진행 상황 로그
|
||||
- **`final_result`**: OCR 처리가 성공적으로 완료되었을 때, 추출된 텍스트와 좌표 정보가 포함된 최종 결과
|
||||
"""
|
||||
# request_id → task_id 매핑 확인
|
||||
task_id = redis_client.hget("ocr_task_mapping", request_id)
|
||||
|
||||
if not task_id:
|
||||
raise HTTPException(
|
||||
status_code=404, detail=f"Meeting ID {request_id} 작업 없음"
|
||||
)
|
||||
|
||||
# Celery 작업 상태 조회
|
||||
result = AsyncResult(task_id, app=celery_app)
|
||||
status = result.status
|
||||
|
||||
# 작업 로그 조회
|
||||
try:
|
||||
logs = redis_client.lrange(f"ocr_status:{request_id}", 0, -1)
|
||||
parsed_logs = [json.loads(log) for log in logs]
|
||||
except Exception as e:
|
||||
parsed_logs = [
|
||||
{
|
||||
"status": "로그 가져오기 실패",
|
||||
"error": str(e),
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
}
|
||||
]
|
||||
|
||||
# 최종 결과 Redis에서 조회
|
||||
final_result = None
|
||||
try:
|
||||
result_str = redis_client.get(f"ocr_result:{task_id}")
|
||||
if result_str:
|
||||
final_result = json.loads(result_str)
|
||||
except Exception as e:
|
||||
final_result = {"error": f"결과 조회 실패: {str(e)}"}
|
||||
|
||||
return JSONResponse(
|
||||
content={
|
||||
"request_id": request_id,
|
||||
"task_id": task_id,
|
||||
"celery_status": status,
|
||||
"progress_logs": parsed_logs,
|
||||
"final_result": final_result,
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user