Files
gpu-services/nanoclaude/services/job_manager.py
Hyungi Ahn 12ca8f19d9 fix: 이메일 멀티 폴더 조회 + Synology 무응답 방지
- email_tool: INBOX/Gmail/Technicalkorea 3개 폴더 순회
  - 폴더별 try/except isolation (하나 실패해도 나머지 조회)
  - UID → folder:uid 형식 (폴더간 충돌 방지)
  - 날짜 parsedate_to_datetime 정렬
  - 폴더 라벨 표시 (개인/Gmail/회사)
- worker: response_sent 플래그 + finally 무응답 방지
  - 어떤 에러 경로에서든 Synology에 최소 1회 응답 보장

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:38:35 +09:00

63 lines
1.8 KiB
Python

"""JobManager — job lifecycle: create, track status, cancel."""
from __future__ import annotations
import asyncio
import uuid
from dataclasses import dataclass, field
from time import time
from models.schemas import JobStatus
@dataclass
class Job:
id: str
message: str
status: JobStatus = JobStatus.queued
created_at: float = field(default_factory=time)
task: asyncio.Task | None = field(default=None, repr=False)
pipeline: bool = True
rewritten_message: str = ""
callback: str = "" # "synology" | ""
callback_meta: dict = field(default_factory=dict) # username, user_id 등
response_sent: bool = False # Synology에 응답 전송 여부
class JobManager:
def __init__(self) -> None:
self._jobs: dict[str, Job] = {}
def create(self, message: str) -> Job:
job_id = uuid.uuid4().hex[:12]
job = Job(id=job_id, message=message)
self._jobs[job_id] = job
return job
def get(self, job_id: str) -> Job | None:
return self._jobs.get(job_id)
def set_status(self, job_id: str, status: JobStatus) -> None:
job = self._jobs.get(job_id)
if job:
job.status = status
def cancel(self, job_id: str) -> bool:
job = self._jobs.get(job_id)
if not job:
return False
if job.status in (JobStatus.completed, JobStatus.failed, JobStatus.cancelled):
return False
job.status = JobStatus.cancelled
if job.task and not job.task.done():
job.task.cancel()
return True
def attach_task(self, job_id: str, task: asyncio.Task) -> None:
job = self._jobs.get(job_id)
if job:
job.task = task
job_manager = JobManager()