
    j"                       U d Z ddlmZ ddlZddlZddlZddlZddlZddlmZm	Z	 ddl
mZ ddlmZmZmZmZmZ ddlmZ erddlmZ  ej,                  e      Zddddd	Zd
ed<    ej6                  d      ZddZddZddZ G d d      Z y)u  CaptureQueue — append/flush 로 중간 생성물을 image_asset 으로 영속화 (Task A4).

저수준 capture(sink)는 spool 파일 경로 + 메타만 append 한다. step/runner 의
generation_context 종료 시 finally 에서 flush() 가:
  1. 독립 SessionLocal() 세션을 열고
  2. 각 항목을 ImageAsset(is_intermediate=True, ...) 로 insert
  3. spool 파일을 최종 generated 경로로 rename
  4. commit / close
한다. 전체를 try/except 로 감싸 어떤 실패도 raise 하지 않는다(독립 non-fatal
세션 — business transaction rollback 무관). 실패 시 rollback + CAPTURE_DIAG 증가.

★ reference_image_ids 는 절대 건드리지 않는다(char/prop lineage 보존). 전체 입력
lineage 는 input_image_ids 컬럼에 JSON 으로 저장한다.
    )annotationsN)datetimetimezone)Path)TYPE_CHECKINGAnyDictListTuple)SessionLocal)GenerationContext)skipped_no_contextskipped_no_flush_scopeskipped_capture_offflush_errorszDict[str, int]CAPTURE_DIAGz[^A-Za-z0-9_.-]c                b    t         j                  d| xs d      }|dv st        |      dhk(  ry|S )u  stage 를 안전한 단일 path 세그먼트로. DB 엔 원문 저장, 파일 경로에만 사용.

    허용 문자 ``[A-Za-z0-9_.-]`` 외는 ``_`` 치환. ``.``/``..``/빈 문자열 등
    상위 디렉터리 탈출 위험 값은 ``unknown`` 으로(generated root 밖 금지)._ )r   .z..r   unknown)_UNSAFE_SEG_REsubset)stagesegs     V/Users/manta/Documents/Projects/TheRoad-I1/backend/app/services/image_capture/queue.py_safe_segmentr   3   s7    
 

S%+2
.C
oScU!2J    c                 d    t        j                  t        j                        j	                         S N)r   nowr   utc	isoformat r   r   _nowr&   >   s    <<%//11r   c                z    ddl m} t        |j                        | z  }|r|dz  |z  }|dz  dz  t	        |      z  S )u   최종 generated 디렉터리(절대). episode 무관 자산은 project-level fallback.

    stage 는 ``_safe_segment`` 로 정화해 경로 탈출을 막는다(Codex BLOCKING 2).r   )settingsepisodesimages	generated)app.core.configr(   r   projects_dirr   )
project_id
episode_idr   r(   bases        r   _generated_dirr1   B   sG     )%%&3Dj :-(?[(=+???r   c                  (    e Zd ZdZddZddZddZy)	CaptureQueueuW   단일 capture scope 의 flush 큐. append=적재, flush=독립세션 일괄 영속화.c                R    || _         g | _        t        j                         | _        y r!   )_ctx_items	threadingLock_lock)selfctxs     r   __init__zCaptureQueue.__init__Q   s     	8: ^^%
r   c                    | j                   5  | j                  j                  |t        |xs i       f       ddd       y# 1 sw Y   yxY w)uF   spool 파일 경로 + 메타를 적재(DB 접근 없음). thread-safe.N)r9   r6   appenddict)r:   
spool_pathmetas      r   r>   zCaptureQueue.appendY   s3    ZZKK
D,<=> ZZs   +AA
c           	        | j                   5  | j                  s
	 ddd       y| j                  }g | _        ddd       | j                  }d}d}	 ddlm}  |        ddlm} t               }D ]  \  }}t        t        j                               }	|j                  d      xs |j                  }
t        |j                  |j                  |
      }|j!                  dd       ||	 dz  }t#        |      }	 |j%                  |       |j                  d	      }|j                  d
      }|t/        |      nd}|j0                   |i }|j3                  d|j0                          |d$i d|	d|j                  d|j                  ddddd|
d|j                  d      d	|t5        j6                  |      ndd|j                  d      d|j                  d      d|j                  d      d|j                  d      d|t5        j6                  |      ndd|j8                  d|j:                  d|j<                  d|j                  d      dt        |      dddt?               }|jA                  |       |d z  } |jC                          ||	 |jE                          S S # 1 sw Y   xY w# t&        $ rD |j)                  |j+                                	 |j-                          n# t&        $ r Y nw xY wY w xY w# tF        $ r Y S w xY w# tF        $ r{ tH        d!xx   d z  cc<   tJ        jM                  d"tO              d#       |!	 |jQ                          n# tF        $ r Y nw xY wY |!	 |jE                          y# tF        $ r Y yw xY wyw xY w# |!	 |jE                          w # tF        $ r Y w w xY ww xY w)%u1  독립 세션으로 적재 항목을 ImageAsset 으로 insert + spool→final rename.

        Returns:
            insert 한 행 수. 어떤 실패도 raise 하지 않는다(non-fatal). 실패 시
            rollback + CAPTURE_DIAG["flush_errors"] 증가 후 0 반환. 큐는 항상 비운다.
        Nr   )register_models)
ImageAssetr   T)parentsexist_okz.pnginput_image_idspipeline_metadatascene_indexidr.   r/   
asset_typer+   is_intermediatepipeline_rolegeneration_call_idcandidate_indexdispositionattempt_indexpipeline_metadata_json	entity_idstill_id
shot_indexprompt_usedprompt	file_pathstatus
created_at   r   um   CaptureQueue.flush failed (non-fatal) — %d item(s) dropped (renamed files may be orphaned in generated dir))exc_infor%   ))r9   r6   r5   app.core.databaserC   app.models.projectrD   r   struuiduuid4getr   r1   r.   r/   mkdirr   renameOSErrorwrite_bytes
read_bytesunlinkr?   rI   
setdefaultjsondumpsrS   rT   rU   r&   addcommitclose	Exceptionr   loggerwarninglenrollback)r:   itemsr;   sessioninsertedrC   rD   r@   rA   aidr   gen_dir
final_pathsp	input_idspipeline_metapmrows                     r   flushzCaptureQueue.flush^   s    ZZ;; Z KKEDK	  ii^	
 :5"nG$) 
D$**,')6SYY(OdT:$#d|3
 *%IIj) !HH%67	 $)< = -:,ET-(4??.zMM-A  "~~  #~~  +	
 %)   #'((?"; 2;1F

9-D (,xx0D'E %)HH->$? !% 7 #'((?";  +-.

2d!$ "mm%& !\\'(  #~~)* !% 2+. "*o/0 '12  $v36 C Au %*x NN$ "MMO #G Z>  **2==?;		" 	N ! )  	 (A-(NNCE
	   "$$&  "MMO   ##	" "MMO   #s   J4J4B)L  :KFL  "L4J>(L*K;:L;	LLLL
L  LL  	LL =N$M/.N$/	M;8N$:M;;N$>N' N 	NN#N$$N' 'O+N<;O<	OOOON)r;   z'GenerationContext'returnNone)r@   r_   rA   zDict[str, Any]r   r   )r   int)__name__
__module____qualname____doc__r<   r>   r   r%   r   r   r3   r3   N   s    a&?
nr   r3   )r   
str | Noner   r_   )r   r_   )r.   r_   r/   r   r   r_   r   r   )!r   
__future__r   rj   loggingrer7   r`   r   r   pathlibr   typingr   r   r	   r
   r   r]   r   "app.services.image_capture.contextr   	getLoggerr   rp   r   __annotations__compiler   r   r&   r1   r3   r%   r   r   <module>r      s    #   	   '  8 8 *D			8	$   n  ./2	@~ ~r   