
    %<i+                         d Z ddlZddl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Z G d d      Zdej                  fd	Zd
 Zedk(  r e        yy)u   
FIFO Event Queue Manager
- CLI와 모듈 import 양쪽으로 사용 가능
- fcntl.flock 기반 파일 잠금으로 동시 쓰기 충돌 방지
- Atomic write (임시 파일 → os.replace)
- JSON 읽기/쓰기 실패 시 .bak 백업 복구 로직
    N)datetimetimezone)Optionalz2/home/jay/workspace/memory/events/event-queue.jsonc            
           e Zd ZddefdZd ZdefdZdefdZdedefd	Z	d
ededededef
dZ
dee   fdZdedee   fdZddedefdZdefdZy)
EventQueueN
queue_filec                 j    |xs t         | _        | j                  dz   | _        | j                          y )Nz.bak)DEFAULT_QUEUE_FILEr   backup_file_ensure_queue_file)selfr   s     C/home/jay/workspace/.worktrees/task-2057-dev2/memory/event-queue.py__init__zEventQueue.__init__   s,    $:(:??V3!    c                     t        j                  t         j                  j                  | j                        d       t         j                  j                  | j                        s| j                  g g d       yy)u6   큐 파일이 없으면 초기 구조로 생성한다.Texist_okqueue	processedN)osmakedirspathdirnamer   exists_write_data)r   s    r   r   zEventQueue._ensure_queue_file!   sM    
BGGOODOO4tDww~~doo.r;< /r   returnc                    	 t        | j                  dd      5 }t        j                  |t        j                         	 |j                         }t        j                  |      }t        j                  |t        j                         	 ddd       j                  dg        |j                  dg        |S # t        j                  |t        j                         w xY w# 1 sw Y   XxY w# t        j                  t        f$ r}t        j                  j                  | j                        r	 t        | j                  dd      5 }t        j                   |      }ddd       n# 1 sw Y   nxY wj                  dg        |j                  dg        | j#                  |       |cY d}~S # t        j                  t        f$ r Y nw xY wt%        d| d      d}~ww xY w)	uI   JSON 파일을 읽는다. 실패 시 백업에서 복구를 시도한다.rutf-8encodingNr   r   u   큐 파일 읽기 실패: u   . 백업도 사용 불가.)openr   fcntlflockLOCK_SHreadjsonloadsLOCK_UN
setdefaultJSONDecodeErrorOSErrorr   r   r   r   loadr   RuntimeError)r   fcontentdataprimary_errbfs         r   
_read_datazEventQueue._read_data'   s|   	doosW= 2Au}}-2ffhG::g.DKK5==12 OOGR(OOK,K	 KK5==12 2 $$g. 	ww~~d../	d..gF -"#yy}- - -OOGR0OOK4$$T*K,,g6 ,[M9ST 	s   C- %C! %B8%$C!
-C- 8&CC!!C*&C- -G")G0F/E'	F/'E0	,=F/)G"/GG
GGG"r2   c                    t         j                  j                  | j                        }t        j                  |d       t         j                  j                  | j                        r+	 t        j                  | j                  | j                         t        j                  |d      \  }}	 t        j                  |dd      5 }t        j                  |t        j                         	 t!        j"                  ||dd	
       |j%                          t        j&                  |j)                                t        j                  |t        j*                         	 ddd       t        j,                  || j                         y# t        $ r Y w xY w# t        j                  |t        j*                         w xY w# 1 sw Y   bxY w# t.        $ r' 	 t        j0                  |        # t        $ r Y  w xY ww xY w)uB   Atomic write: 임시 파일에 쓴 뒤 os.replace로 교체한다.Tr   z.tmp)dirsuffixwr    r!   F   ensure_asciiindentN)r   r   r   r   r   r   shutilcopy2r   r-   tempfilemkstempfdopenr$   r%   LOCK_EXr(   dumpflushfsyncfilenor*   replace	Exceptionunlink)r   r2   dir_pathfdtmp_pathtfs         r   r   zEventQueue._write_dataF   sm   77??4??3
Ht, 77>>$//*T__d.>.>?
  ''HVDH	2sW5 3B.3IIdBU1EHHJHHRYY[)KKEMM23 JJx1   KKEMM23 3  			(#   		ss   +*F 0G %G .AF:$G (G 	FF&F==G  G	G 	G<G,+G<,	G85G<7G88G<c                    d}|j                  dg       |j                  dg       z   }|D ]<  }|j                  dd      }|j                  d      s'	 t        |dd       }||kD  r|}> d|d	z   d
S # t        $ r Y Sw xY w)uK   queue + processed 전체에서 최대 시퀀스 번호를 찾아 +1 반환.r   r   r   id zevt-   N   03d)get
startswithint
ValueError)r   r2   max_seq
all_eventsevteidseqs          r   _next_event_idzEventQueue._next_event_idg   s    XXgr*TXXk2-FF
 	C''$#C~~f%c!"g,CW}"%	 gk#&'' " s   A22	A>=A>
event_typetask_idteam_idreport_pathc           
         t        j                  t         j                  j                  | j                        d       | j                  dz   }t        |dd      5 }t        j                  |t        j                         	 | j                         }| j                  |      }|||||dt        j                  t        j                        j                  d      d	}	|d
   j!                  |	       | j#                  |       t        j                  |t        j$                         	 ddd       |	S # t        j                  |t        j$                         w xY w# 1 sw Y   	S xY w)uJ   이벤트를 큐에 추가하고 추가된 이벤트 dict를 반환한다.Tr   .lockr9   r    r!   pending%Y-%m-%dT%H:%M:%S+00:00)rP   typer`   teamreportstatus
created_atr   N)r   r   r   r   r   r#   r$   r%   rC   r5   r^   r   nowr   utcstrftimeappendr   r*   )
r   r_   r`   ra   rb   	lock_pathlock_fr2   event_idevents
             r   enqueuezEventQueue.enqueuez   s    	BGGOODOO4tDOOg-	)S73 	3vKK.3(..t4"&&#)'"*,,x||"<"E"E1#
 W$$U+  &FEMM2'	3*  FEMM2'	3* s%   %EB D1$E1&EEE$c                 h    | j                         }|d   D ]  }|j                  d      dk(  s|c S  y)uG   pending 상태인 첫 번째 이벤트를 반환한다. 없으면 None.r   rj   re   N)r5   rU   )r   r2   r[   s      r   peekzEventQueue.peek   s<     = 	Cwwx I-
	 r   rr   c                    | j                   dz   }t        |dd      5 }t        j                  |t        j                         	 | j                         }d}g }|d   D ]*  }|j                  d      |k(  r|}|j                  |       , |.	 t        j                  |t        j                         ddd       yd|d	<   t        j                  t        j                        j                  d
      |d<   ||d<   |d   j                  |       | j                  |       t        j                  |t        j                         	 ddd       |S # t        j                  |t        j                         w xY w# 1 sw Y   S xY w)uI   event_id 이벤트를 queue에서 제거하고 processed로 이동한다.rd   r9   r    r!   Nr   rP   r   rj   rf   processed_at)r   r#   r$   r%   rC   r5   rU   ro   r*   r   rl   r   rm   rn   r   )r   rr   rp   rq   r2   target	new_queuer[   s           r   dequeuezEventQueue.dequeue   sL   OOg-	)S73 	3vKK.3(	= .Cwwt}0!$!((-	. > FEMM2/	3 	3 $/x )1hll)C)L)L-*~& !*W[!((0  &FEMM2/	32  FEMM2/	32 s1   %E6AE$E6:A$E$E6&E33E66F include_processedc                 J    | j                         }|r|d   |d   dS d|d   iS )u!   이벤트 목록을 반환한다.r   r   r   )r5   )r   r|   r2   s      r   list_eventszEventQueue.list_events   s;     g!+.  g''r   c                 L    | j                         }t        d |d   D              S )u&   pending 이벤트 수를 반환한다.c              3   J   K   | ]  }|j                  d       dk(  sd  yw)rj   re   rS   N)rU   ).0r[   s     r   	<genexpr>z#EventQueue.count.<locals>.<genexpr>   s%      
#''(*;y*HA
s   ##r   )r5   sum)r   r2   s     r   countzEventQueue.count   s,      
g
 
 	
r   )N)F)__name__
__module____qualname__strr   r   dictr5   r   r^   rt   r   rv   r{   boolr~   rW   r    r   r   r   r      s    "3 "=D > B(4 (C (&!! ! 	!
 ! 
!Fhtn   <(T (d (
s 
r   r   r   c                     t        j                  dd      } | j                  dd      }|j                  dd	      }|j	                  d
ddd       |j	                  ddd       |j	                  ddd       |j	                  ddd       |j	                  dd d       |j                  dd	      }|j	                  dd        |j                  dd	      }|j	                  dd	       |j	                  dd        |j                  d d!	      }|j                         }|j	                  d"d#dd$%       |j	                  d&d'd#d()       |j	                  dd        |j                  d*d+	      }|j	                  dd        | S ),Nzevent-queue.pyzFIFO Event Queue Manager)progdescriptioncommandCOMMAND)destmetavarrt   u    이벤트를 큐에 추가한다)helpz--typer_   Tu%   이벤트 타입 (예: task_complete))r   requiredr   z	--task-idu   태스크 ID (예: task-42))r   r   z--teamu   팀 ID (예: dev1-team)z--reportu   리포트 파일 경로z--queue-fileu+   큐 파일 경로 (기본값 사용 가능))defaultr   rv   u;   다음 처리할 이벤트를 조회한다 (제거 안 함))r   r{   u&   이벤트를 processed로 이동한다rr   u   이벤트 ID (예: evt-001)listu   큐 상태를 조회한다z	--pending
store_trueu    pending 이벤트만 (기본값))actionr   r   z--allrZ   u   queue + processed 모두)r   r   r   r   u%   pending 이벤트 수를 출력한다)argparseArgumentParseradd_subparsers
add_parseradd_argumentadd_mutually_exclusive_group)parser
subparsersp_enqp_peekp_deqp_listgroupp_cnts           r   build_parserr      s   $$.F &&Iy&IJ !!)2T!UE	xlTHop	{T8UV	x$5NO	zD7PQ	~t:gh ""60m"nF
5 !!)2Z![E	z(EF	~t4 ""60L"MF//1E	{<Lno	w\,Mgh
5 !!'0W!XE	~t4Mr   c                     t               } | j                         }|j                  %| j                          t	        j
                  d       t        |dd       }t        |      }|j                  dk(  r_|j                  |j                  |j                  |j                  |j                        }t        t        j                  |dd             y |j                  d	k(  rX|j!                         }|$t        t        j                  d
didd             y t        t        j                  |dd             y |j                  dk(  r|j#                  |j$                        }|Ft        t        j                  dd|j$                   idd             t	        j
                  d       y t        t        j                  |dd             y |j                  dk(  rAt        |dd      }|j'                  |      }t        t        j                  |dd             y |j                  dk(  rt        |j)                                y | j                          t	        j
                  d       y )Nr   r   )r   rt   )r_   r`   ra   rb   Fr:   r;   rv   rj   emptyr{   erroru&   이벤트를 찾을 수 없습니다: rS   r   rZ   )r|   r   )r   
parse_argsr   
print_helpsysexitgetattrr   rt   r_   r`   rh   ri   printr(   dumpsrv   r{   rr   r~   r   )r   argsr   eqresultr|   s         r   mainr      s   ^FD|||T2J	z	*B||y LLII	  
 	djjeA>?		>$**h0uQOP$**V%BC		"DMM*>

 Ft}}oVW!& HHQK$**V%BC		#D,>2CDdjjeA>?		 bhhj 	r   __main__)__doc__r   r$   r(   r   r>   r   r@   r   r   typingr   r
   r   r   r   r   r   r   r   r   <module>r      sg       	  
  ' I {
 {
D#h-- #L3l zF r   