
    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mZmZ ddl	m
Z
 ej                  j                  d e e
e      j                         j                   j                               Z e e
e      dz  dz        Zefded	ed
e
fdZde
d
efdZde
ded
dfdZd
efdZd
efdZd
efdZd
efdZd Zd Zedk(  r e        yy)u<  
drive-change-log.py - Google Drive 업로드 변경사항 로컬 로그 관리

Cloud Functions의 crawlYoutubeChannels가 Drive에 파일을 업로드할 때,
해당 변경사항을 로컬 로그에 기록하여 project-map incremental 시스템이 참조할 수 있게 합니다.

Usage:
    # 변경 기록 추가
    python3 drive-change-log.py add --project insuwiki --path "04_유튜브요약/보험명의정닥터/260301_영상제목_요약.md" --action upload

    # 미처리 변경 목록 조회
    python3 drive-change-log.py list --project insuwiki [--unprocessed]

    # 처리 완료 마킹
    python3 drive-change-log.py mark-processed --project insuwiki --ids "id1,id2"

    # 로그 정리 (30일 이상 된 처리 완료 항목 삭제)
    python3 drive-change-log.py cleanup --project insuwiki [--days 30]
    N)datetime	timedeltaPathWORKSPACE_ROOTmemoryzdrive-changes
project_idlog_dirreturnc                 $    t        |      |  dz  S )u<   프로젝트 ID에 해당하는 로그 파일 경로 반환.z.jsonlr   )r	   r
   s     I/home/jay/workspace/.worktrees/task-2116-dev1/scripts/drive-change-log.pyget_log_pathr   $   s    =j\000    log_pathc                 (   | j                         sg S g }t        | dd      5 }|D ]:  }|j                         }|s	 |j                  t	        j
                  |             < 	 ddd       |S # t        j                  $ r Y ]w xY w# 1 sw Y   |S xY w)u(   JSONL 파일에서 모든 항목 읽기.rutf-8encodingN)existsopenstripappendjsonloadsJSONDecodeError)r   entriesflines       r   read_log_linesr    )   s    ??	G	hg	. 	! 	D::<Dtzz$/0		 N '' 	 Ns.   B$A.!B.BBBBBr   c                    | j                   j                  dd       t        |       dz   }	 t        |dd      5 }|D ]+  }|j	                  t        j                  |d      d	z          - 	 d
d
d
       t        j                  |t        |              y
# 1 sw Y   )xY w# t        $ r' 	 t        j                  |        # t        $ r Y  w xY ww xY w)u;   JSONL 파일에 항목 전체 다시 쓰기 (덮어쓰기).Tparentsexist_okz.tmpwr   r   Fensure_ascii
N)parentmkdirstrr   writer   dumpsosrename	ExceptionunlinkOSError)r   r   tmp_pathr   entrys        r   write_log_linesr5   ;   s    OO$68}v%H
(C'2 	Fa  F

5u=DEF	F 			(CM*	F 	F  	IIh 	  		sF   B! 1B-'B! BB! !	C+C C	C
CCCc                    t        | j                  | j                        }|j                  j	                  dd       t        t        j                               | j                  | j                  | j                  t        j                         j                  d      dd}t        |      dz   }	 t        |d      5 }t        j                  |t        j                          	 |j                  j	                  dd       t        t        |      d	d
      5 }|j#                  t%        j&                  |d      dz          ddd       t        j                  |t        j(                         	 ddd       d|dS # 1 sw Y   ;xY w# t        j                  |t        j(                         w xY w# 1 sw Y   CxY w# t*        $ r}dd| dcY d}~S d}~ww xY w)u   변경 기록 추가.Tr"   seconds)timespecF)idprojectpathaction	timestamp	processed.lockr%   ar   r   r&   r(   Nerroru   로그 쓰기 실패: statusmessageok)rC   r4   )r   r:   r
   r)   r*   r+   uuiduuid4r;   r<   r   now	isoformatr   fcntlflockLOCK_EXr,   r   r-   LOCK_UNr2   )argsr   r4   	lock_pathlock_fdr   es          r   cmd_addrR   L   s{   DLL$,,7HOO$6 $**,<<		++\\^--y-AE H'I
L)S! 	4WKK/4%%dT%B#h-w? J1GGDJJu5ADHIJ GU]]3	4 U++J J GU]]3	4 	4  L!0Fqc.JKKLsf   1F< =%F0#4F*E;F	$F0.F< ;F	 F&F--F00F95F< <	GGGGc                 t   t        | j                  | j                        }t        |      dz   }	 t	        |d      5 }t        j                  |t
        j                         	 t        |      }t        j                  |t
        j                         	 ddd       | j                  r"D cg c]  }|j                  dd      r| }}d	| j                  t              |d
S # t        j                  |t
        j                         w xY w# 1 sw Y   zxY w# t        $ r}dd| dcY d}~S d}~ww xY wc c}w )u   변경사항 목록 조회.r?   r%   NrA   u   로그 읽기 실패: rB   r>   FrE   )rC   r:   countr   )r   r:   r
   r+   r   rJ   rK   LOCK_SHr    rM   r2   unprocessedgetlen)rN   r   rO   rP   r   rQ   s         r   cmd_listrY   k   s   DLL$,,7HH'IL)S! 	4WKK/4(2GU]]3	4 %GQUU;-F1GGt||c'lW^__ GU]]3	4 	4  L!0Fqc.JKKL HsX   D %D"C"-$DD *D5D5"&DDDD 	D2 D-'D2-D2c                    t        | j                  | j                        }|j                         sdd| dS t	        d | j
                  j                  d      D              }|sdddS t        |      dz   }d}g }	 t        |d	      5 }t        j                  |t        j                         	 t        |      }|D ch c]  }|j                  d
       }	}|D ]  }
|
|	vs|j                  |
        g }|D ]0  }|j                  d
      |v r
d|d<   |dz  }|j                  |       2 t        ||       t        j                  |t        j                          	 ddd       d| j                  |d}|r||d<   |S c c}w # t        j                  |t        j                          w xY w# 1 sw Y   PxY w# t"        $ r}dd| dcY d}~S d}~ww xY w)u5   특정 ID의 변경사항을 처리 완료로 마킹.rA   u   로그 파일 없음: rB   c              3   ^   K   | ]%  }|j                         s|j                          ' y w)N)r   ).0is     r   	<genexpr>z%cmd_mark_processed.<locals>.<genexpr>   s     JA	aggiJs   --,u   마킹할 ID가 없습니다.r?   r   r%   r9   Tr>      Nu   처리 완료 마킹 실패: rE   )rC   r:   marked	not_found)r   r:   r
   r   setidssplitr+   r   rJ   rK   rL   r    rW   r   r5   rM   r2   )rN   r   ids_to_markrO   marked_countrb   rP   r   rQ   id_set	target_idupdatedr4   results                 r   cmd_mark_processedrl      s   DLL$,,7H??!0Fxj.QRRJ)<JJK!.MNNH'ILIS)S! 	4WKK/4(2/67!!%%+77!, 4I .!((34 $ *Eyy+5-1k*$)NN5)	*  '2GU]]3%	4, NF'{M+ 8 GU]]3%	4 	4&  S!0MaS.QRRSsg   >F9 
%F-0F?E?F#AF9$F-F9 ?F&F**F--F62F9 9	GG	GGc                    t        | j                  | j                        }|j                         sd| j                  dddS | j                  }t        j                         t        |      z
  }t        |      dz   }d}	 t        |d      5 }t        j                  |t        j                         	 t        |      }g }|D ]W  }	|	j                  dd	      r2	 t        j                  |	j                  d
d            }
|
|k  r|dz  }F	 |j%                  |	       Y t'        ||       t        j                  |t        j(                         	 ddd       d| j                  ||dS # t         t"        f$ r Y nw xY w# t        j                  |t        j(                         w xY w# 1 sw Y   XxY w# t*        $ r}dd| dcY d}~S d}~ww xY w)u&   오래된 처리 완료 항목 삭제.rE   r   u   로그 파일 없음)rC   r:   deletedrD   )daysr?   r%   r>   Fr=    r`   NrA   u   정리 실패: rB   )rC   r:   rn   ro   )r   r:   r
   r   ro   r   rH   r   r+   r   rJ   rK   rL   r    rW   fromisoformat
ValueError	TypeErrorr   r5   rM   r2   )rN   r   ro   cutoffrO   deleted_countrP   r   keptr4   tsrQ   s               r   cmd_cleanuprx      s   DLL$,,7H??4<<ARhii99D\\^iT22FH'IME)S! 	4WKK/4(2$ 'Eyye4!!)!7!7		+r8R!SB!F{ - 2 (  + KK&'  $/GU]]3'	40 << 	  !+I6 ! ! GU]]3'	4 	4(  E!s.CDDEsr    F/ %F#2$E:/E%!E:'$F#F/ %E74E:6E77E::&F  F##F,(F/ /	G
8G?G
G
c                     t        j                  d      } | j                  dt        dt         d       | j	                  dd	      }d
|_        |j                  dd      }|j                  dd
d       |j                  dd
d       |j                  ddg dd       |j                  dd      }|j                  dd
d       |j                  dddd       |j                  d d!      }|j                  dd
d       |j                  d"d
d#       |j                  d$d%      }|j                  dd
d       |j                  d&t        d'd()       | j                         S )*Nu8   Google Drive 업로드 변경사항 로컬 로그 관리)descriptionz	--log-diru'   로그 파일 디렉토리 (기본값: ))defaulthelpcommandu	   명령어)destr}   Taddu   변경 기록 추가)r}   z	--projectu   프로젝트 ID (예: insuwiki))requiredr}   z--pathu#   Drive 파일 경로 (상대 경로)z--actionupload)r   updatedeleteu!   변경 유형 (기본값: upload))r|   choicesr}   listu   변경사항 목록 조회u   프로젝트 IDz--unprocessed
store_trueFu   미처리 변경 건만 조회)r<   r|   r}   mark-processedu   처리 완료 마킹z--idsu*   처리 완료할 ID 목록 (쉼표 구분)cleanupu%   오래된 처리 완료 항목 삭제z--days   u'   삭제 기준 일수 (기본값: 30일))typer|   r}   )	argparseArgumentParseradd_argumentDEFAULT_LOG_DIRadd_subparsersr   
add_parserint
parse_args)parser
subparsersr   list_parsermark_parsercleanup_parsers         r   r   r      s   $$NF 66GqI   &&IK&HJJ &&u3I&JJK$=^_Ht:_`.0	   ''5Q'RK[4>OP-	   ''(8?U'VK[4>OPWt:fg  **9;b*cNdARS6	    r   c                     t               } t        t        t        t        d}|j                  | j                        }|dd| j                   d}n		  ||       }t        t        j                  |dd             |j                  d      d	k7  rt        j                  d
       y y # t        $ r}dt        |      d}Y d }~hd }~ww xY w)N)r   r   r   r   rA   u   알 수 없는 명령: rB   F   )r'   indentrC   rE   r`   )r   rR   rY   rl   rx   rW   r~   r0   r+   printr   r-   sysexit)rN   command_maphandlerrk   rQ   s        r   mainr     s    <D ,	K oodll+G#2I$,,0XY	<T]F 
$**V%
:;zz(t# $  	< 'CF;F	<s   B' '	C0CC__main__)__doc__r   rJ   r   r.   r   rF   r   r   pathlibr   environrW   r+   __file__resolver)   _WORKSPACE_ROOTr   r   r   r    r5   dictrR   rY   rl   rx   r   r   __name__ r   r   <module>r      s  (    	 
  (  **..!13tH~7M7M7O7V7V7]7]3^_d?+h6HI 2A 1S 13 1T 1
T d $d T d ",T ,>`d `*) )X* *Z1h2 zF r   