
    +Q_js                        U 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Zd dlZ	d dl
Z	d dlZ	d dlmZmZ dZdZe dZe dZej&                  j)                  ej&                  j+                  e            Zej&                  j)                  e      Zej&                  j3                  ed      Zej&                  j3                  ed      Zd	d	d
ddddddddddddZeeef   ed<   dddddddddd d!d"d#d$dZeeef   ed%<   d&Z d'Z!d(Z"d)d*hZ#d+d,d-d.d/d0d1d2d3d4d5d6d7d8d9Z$eeef   ed:<   d;Z%i Z&eeeeeef   ef   ed<<   i Z'eeef   ed=<   d>ed?efd@Z(dAed?efdBZ)dCedz  dDed?edz  fdEZ*dFedz  d?e+fdGZ,dHed?efdIZ-	 	 dkdJedKedLeee.f   dz  d?eee.f   fdMZ/dNed?efdOZ0dNedPed?e+fdQZ1dRedSedTed?edz  fdUZ2dRed?e+fdVZ3dTed?edz  fdWZ4dXeeef   dHedYe5e   d?edz  fdZZ6d[ed?eeeeef   f   fd\Z7	 	 	 	 	 dld]edTedSed^edz  dRedz  d_e8d`e9d?efdaZ:	 	 	 	 dmdbedcedde+dee+dfeeeeef   f   dz  dge+d?dfdhZ;di Z<e=djk(  r e<        yy)n    N)DictTuplez$5b9840fc-3a85-4ab9-bc59-424add794a2fzhttps://api.deepl.comz/v2/translatez/v3/glossaries	translateglossaryz	ko_zh.tsvz	ko_en.tsvz	ko_fr.tsvz	ko_de.tsvz	ko_ja.tsvz	ko_pt.tsvz	ko_es.tsvz	ko_tr.tsvz	ko_ru.tsvz	ko_vi.tsvz	ko_ms.tsvz	ko_th.tsvz	ko_id.tsv)ZHZH-HANTENFRDEJAPT-PTESTRRUVIMSTHIDGLOSSARY_FILESzhenfrdejaptestrruvimsthidGLOSSARY_LANG_CODESKeyz
Korean(ko)zShared Comments -r   r   r	   r
   r   r   r   r   r   r   r   r   r   r   )zChinese (Simplified)(zh)zChinese (Traditional)(zh-TW)zEnglish(en)z
French(fr)z
German(de)zJapanese(ja)zPortuguese(pt)zSpanish(es)zTurkish(tr)zRussian(ru)zVietnamese(vi)z	Malay(ms)zThai(th)zIndonesian(id)LANG_COLUMNSz	 CommentsTRANSLATION_CACHEGLOSSARY_CACHEpathreturnc                 4    t        j                  | d       | S )u8   디렉토리가 없으면 생성하고 경로를 반환.Texist_ok)osmakedirs)r*   s    ./home/ubuntu/springtime-server/script/deepl.py
ensure_dirr2   f   s    KKt$K    output_rootc                     t         j                   j                         j                  d      }t        t        j
                  j                  | |            S )uP   이번 실행 결과를 저장할 타임스탬프 디렉토리 경로를 생성.z%Y%m%d%H%M%S)datetimenowstrftimer2   r/   r*   join)r4   	timestamps     r1   build_run_output_dirr;   l   s;    !!%%'00@Ibggll;	:;;r3   previous_rootfilenamec                 T   | syt         j                  j                  |       rCt         j                  j                  | |      }t         j                  j	                  |      r|S dS t         j                  j	                  |       r$t         j                  j                  |       |k(  r| S y)u;   이전 번역 재사용 대상 CSV 파일 경로를 결정.N)r/   r*   isdirr9   isfilebasename)r<   r=   candidate_paths      r1   resolve_previous_csv_pathrC   r   su    	ww}}]#mX>!#!?~ITI	ww~~m$)9)9-)HH)Tr3   valuec                 L    | xs dj                         j                         dk(  S )u*   Comments 컬럼 값이 PASS 인지 확인.r%   PASS)stripupper)rD   s    r1   is_pass_commentrI      s#    KR &&(F22r3   col_namec                     | t         z   S )u8   언어 컬럼에 대응하는 Comments 컬럼명 반환.)COMMENTS_COL_SUFFIX)rJ   s    r1   get_language_comments_colrM      s    )))r3   urlmethoddatac                    dt          dd}d}|)t        j                  |      j                  d      }d|d<   t        j
                  j                  | |||      }t        j
                  j                  |d	
      5 }|j                         j                  d      }|j                         si cddd       S t        j                  |      cddd       S # 1 sw Y   yxY w)u   DeepL API 공통 요청.DeepL-Auth-Key CopyIt-Translate/1.0)Authorization
User-AgentNutf-8application/jsonContent-Type)rP   rO   headers   timeout)DEEPL_API_KEYjsondumpsencodeurllibrequestRequesturlopenreaddecoderG   loads)rN   rO   rP   rY   bodyreqresp	resp_bodys           r1   _api_requestrl      s     +=/:,G Dzz$&&w/"4
..
 
 4
 
PC			R		0 %DIIK&&w/	 % % zz)$	% % %s   >1C9CC entriesc                     g }| j                  dd      j                  dd      j                  d      D ]&  }|j                         }|s|j                  |       ( dj	                  |      S )u<   Glossary TSV 비교를 위해 줄바꿈/공백을 정규화.

)replacesplitrG   appendr9   )rm   normalized_lineslinestrippeds       r1   _normalize_glossary_entriesrx      sh    -55dDAGGM .::<##H-. 99%&&r3   glossary_pathc                 
   t        | j                  dd      j                  dd      j                  d      d      D ]  \  }}|j                         }|s|j	                  d      dk7  r?t        d| d| d	|j	                  t        d
             dt        j                          y|j                  dd      \  }}|j                         r|j                         rt        d| d| dt        j                          y y)u8   Glossary TSV가 DeepL 형식에 맞는지 사전 검증.ro   rp   rq      start	u"   [WARN] 잘못된 glossary 형식: :u    (탭 1개 필요, 현재 	   u   개)fileFu$    (원문/번역문이 모두 필요)T)		enumeraterr   rs   rG   countprintchrsysstderr)rm   ry   line_numberraw_linerv   source_texttarget_texts          r1   _validate_glossary_entriesr      s   !*%--dD9??E" X ~~>>$1$4]O1[M R,,4NN3q6,B+C4IZZ
 #+>>$#: [  "+*;*;*=4]O1[M R6 6ZZ
 /2 r3   glossary_idsource_langtarget_langc                    t         j                  j                  |j                         |j                         d      }t         d|  d| }t        |d      }|j                  d      xs g }|sy|d   j                  d	      S )
u#   DeepL glossary 엔트리를 조회.)r   r   /z	/entries?GETrO   dictionariesNr   rm   )ra   parse	urlencodelowerDEEPL_GLOSSARIES_ENDPOINTrl   get)r   r   r   queryrN   resultr   s          r1   _get_glossary_entriesr      s    LL""&,,.&,,.	
E ''qYug
FC#e,F::n-3L?y))r3   c                     	 t        t         d|  d       y# t        $ r,}t        d|  d| dt        j
                         Y d	}~y
d	}~ww xY w)u   DeepL glossary 삭제.r   DELETEr   Tu   [WARN] Glossary 삭제 실패:  ()r   NF)rl   r   	Exceptionr   r   r   )r   es     r1   _delete_glossaryr      sR    12!K=A(S /}BqcC#**Us    	A"A		Ac           	      |   | t         v r	t         |    S t        j                  |       }|syt        j                  j                  t        |      }t        j                  j                  |      sy	 t        |dd      5 }|j                         j                         }ddd       syt        ||      sy| dk(  rdn| }d| }t        j                  | | j                               }	 t        t         d	      }|j                  d
g       D ]  }	|	j                  d      |k(  s|	j                  d      }
|
s-t#        |
d|      }|Bt%        d| t&        j(                         |
t         | <   |dk(  r|
t         d<   |
t         d<   |
c S t+        |      t+        |      k(  r$|
t         | <   |dk(  r|
t         d<   |
t         d<   |
c S t%        d| t&        j(                         t-        |
      s$|
t         | <   |dk(  r|
t         d<   |
t         d<   |
c S  n 	 |d||ddgd}t        t         d|      }|j                  d      }
|
r"|
t         | <   |dk(  r|
t         d<   |
t         d<   |
S 	 y# 1 sw Y   xY w# t        $ r Y yw xY w# t.        $ r Y |w xY w# t0        j2                  j4                  $ rY}|j                         j7                  dd      }t%        d|  d|j8                   d| t&        j(                         Y d}~yd}~wt.        $ r+}t%        d|  d| t&        j(                         Y d}~yd}~ww xY w)uL  
    target_lang 에 해당하는 glossary 파일이 있으면
    DeepL API 로 glossary 를 생성/조회하여 glossary_id 반환.
    같은 이름의 glossary 가 이미 있어도 로컬 TSV 와 내용이 다르면 삭제 후 재생성.
    ZH-HANT는 API에서 zh-Hant 미지원이므로 ZH와 동일 glossary(zh) 사용.
    NrrV   encodingr   r   z
CopyIt-ko-r   r   
glossariesnamer   kou;   [WARN] 기존 glossary 엔트리 조회 실패, 재사용: r   u1   [INFO] glossary 내용 변경 감지, 재생성: tsv)r   r   rm   entries_format)r   r   POST)rO   rP   rr   errorsu   [WARN] Glossary 생성 실패 (z): HTTP  z): )r)   r   r   r/   r*   r9   GLOSSARY_DIRr@   openre   rG   OSErrorr   r#   r   rl   r   r   r   r   r   rx   r   r   ra   error	HTTPErrorrf   code)r   r=   ry   frm   glossary_lang_keyglossary_nametgt_langr   ggidexisting_entriescreate_datar   response_bodys                  r1   _get_or_create_glossaryr      sT    n$k**!!+.HGGLLx8M77>>-(-w7 	'1ffhnn&G	'
 %g}= !,y 8k !2 34M"&&{K4E4E4GHH%7FL"- !	AuuV}-eeM*'<S$'Q$'/YZgYhi!$ 7:{3,436N408;N95"
23CDHcdkHll69{3,436N408;N95"
KM?[ ZZ ,C069{3,436N408;N95"
C!	LV! $(#+&&+	

 7[Yjj'*-N;' D('*t$,/y)J  m	' 	' f  0 <<!! 
	B-k](166(!M?[	
 	
   V/}CsC#**UUVs   /I5 =I(I5 ';J #J 7AJ :J AJ J AJ (I2-I5 5	J J	JJL;0ALL;!L66L;row
fieldnamesc                     t        |      }||v r'| j                  |      xs dj                         }|r|S t        |v r+| j                  t              xs dj                         }|r|S y)u   
    해당 언어 번역 시 사용할 context 를 반환.
    우선순위: 언어별 Comments (값 있음) > Shared Comments (값 있음) > None
    r%   N)rM   r   rG   SHARED_COMMENTS_COL)r   rJ   r   comments_collang_commentshareds         r1   get_context_for_languager   P  sm     -X6Lz!-3::< j(''-.4";;=Mr3   	prev_pathc                    i }t         j                  j                  |       s|S 	 t        | ddd      5 }t	        j
                  |      }|D ]4  }||j                  t              xs dj                         }|s0|||<   6 	 ddd       |S # 1 sw Y   |S xY w# t        $ r-}t        d|  d| dt        j                  	       Y d}~|S d}~ww xY w)
u~   
    이전에 생성된 CSV 파일을 읽어서
    Key 기준으로 전체 row 를 딕셔너리에 저장해서 반환.
    r   	utf-8-sigr%   r   newlineNu+   [WARN] 이전 결과 파일 로드 실패: r   r   r   )r/   r*   r@   r   csv
DictReaderr   KEY_COLrG   r   r   r   r   )r   	prev_rowsf_prevreaderr   keyr   s          r1   load_previous_rowsr   i  s    
 ,.I77>>)$`)S;C 	%v^^F+F %;www'-2446!$	#%	% 	%   `;I;b1MTWT^T^__`s5   B ABB BB B 	C$"CCtextcontextretries	sleep_secc                    | sy| ||xs d|xs df}|t         v r	t         |   S | g||d}|r#|j                         r|j                         |d<   |r||d<   t        j                  |      j	                  d      }	t
        j                  j                  t        |	d      }
|
j                  dd	t                |
j                  d
d       |
j                  dd       t        |      D ]  }	 t
        j                  j                  |
d      5 }|j                         j                  d      }t        j                  |      }|j!                  d      xs g }|st#        d|       |d   j!                  dd      }|t         |<   |cddd       c S  y# 1 sw Y   nxY w# t
        j$                  j&                  $ r}d}	 |j                         j                  dd      }n# t(        $ r Y nw xY w||dz
  k(  rgt+        d| dd  d| d|j,                   d|j.                   t0        j2                         |rt+        d| t0        j2                         | cY d}~c S t5        j6                  |       Y d}~d}~wt(        $ rX}||dz
  k(  r0t+        d| dd  d| d| t0        j2                         | cY d}~c S t5        j6                  |       Y d}~d}~ww xY w) u	  
    DeepL API 를 이용해 한국어(text)를 target_lang 으로 번역.
    context: 다의어 해석 개선용. glossary_id: 고유명사/용어 사전 적용.
    같은 (text, target_lang, context, glossary_id) 조합은 캐시에 저장해서 재사용.
    r%   )r   r   r   r   r   rV   r   )rP   rO   rT   rR   rX   rW   rU   rS   rZ   r[   translationszEmpty translations: r   r   Nrr   r   r{   z[DeepL Error] text='   z...' target=z HTTP r   r   z[DeepL Response] z error=)r(   rG   r^   r_   r`   ra   rb   rc   DEEPL_ENDPOINT
add_headerr]   rangerd   re   rf   rg   r   RuntimeErrorr   r   r   r   r   reasonr   r   timesleep)r   r   r   r   r   r   r   	cache_keyrP   encoded_datarb   attemptresponserk   	resp_jsonr   translated_textr   err_bodys                      r1   translate_textr     s    {GMr;3D"EI%% ++ ""D
 7==?!--/Y)]::d#**73Lnn$$^,v$VG/-(IJ~'9:|%;<> '"&	"'''< 	'$MMO227;	 JJy1	(}}^<B#&)=i['IJJ".q/"5"5fb"A/>!),&	' 	''"	' 	' 	' ||%% 	H668??79?E 'A+%*49+\+ OFF81QXXJ0
 -hZ8szzJJJy! 
	"'A+%*49+ 6)]'!6 JJy!!
	"sz   ,!F!A9F	F!F	F!!K >I<!G#"I<#	G/,I<.G//A*I<K !I<<K 0K8K  KK 
input_pathoutput_pathshow_spinnerrenewprevious_rows	write_bomc                 	   t        | ddd      5 }t        j                  |      }|j                  }|s(t	        d|  t
        j                         	 ddd       yg }	t        |      }
t        |
      }g d}t        |
d	
      D ]p  \  }}|
|j                  t              xs dj                         }|j                  t              xs dj                         }t        |v xr t        |j                  t                    }t         j#                         D ch c]+  }||v r%t        |j                  t%        |                  r|- }}|ri|	j'                  |       |rT|dkD  rO||t        |      z     }| d| }d| dt(        j*                  j-                  |        d| d}t	        |dd       3d}|sr|p|rn|j                  |      }|[|j                  t              xs dj                         }||k(  r/|r-t         j#                         D ]  }||v s||vs||   ||<    d}|ri|	j'                  |       |rT|dkD  rO||t        |      z     }| d| }d| dt(        j*                  j-                  |        d| d}t	        |dd       |si|	j'                  |       |rT|dkD  rO||t        |      z     }| d| }d| dt(        j*                  j-                  |        d| d}t	        |dd       t         j/                         D ]p  \  }}||vr||v r|j                  |      xs d}|j                         }|s|t0        vr|dk7  rEt3        |||      }t5        |      }t7        ||||      }|||<   r |	j'                  |       |s|dkD  s"||t        |      z     }| d| }d| dt(        j*                  j-                  |        d| d}t	        |dd       s 	 ddd       t)        j8                  t(        j*                  j;                  |      d       |rdnd} t        |d| d      5 }!t        j<                  |!      }"|"j?                          |"jA                  	       ddd       |r8t	        djC                  t(        j*                  j-                  |                    yyc c}w # 1 sw Y   xY w# 1 sw Y   UxY w)u  
    한 개의 CSV 파일을 읽어서,
    - 각 row 의 Korean(ko) 컬럼을 기준으로
    - 나머지 언어 컬럼(위에 정의된 LANG_COLUMNS) 값이 비어 있을 경우 DeepL 로 번역해서 채운 뒤
    - 동일한 헤더 구조로 output_path 에 저장.
    r   r   r%   r   u/   [WARN] 빈 헤더 또는 잘못된 CSV 형식: r   N)r&   \|r   r{   r|   r   r   z[u   ] 번역 중(PASS 유지): r   r   T)endflushFu'   ] 번역 중(이전 결과 재사용): u   ] 번역 중: )r   r   r   r-   rV   w)r   u   [✔] 번역 완료: {})"r   r   r   r   r   r   r   listlenr   r   
KOREAN_COLrG   r   r   rI   r'   keysrM   rt   r/   r*   rA   itemsBLANK_VALUESr   r   r   r0   dirname
DictWriterwriteheader	writerowsformat)#r   r   r   r   r   r   f_inr   r   rowsrow_list
total_rowsspinner_charsidxr   r   key_valshared_passrJ   pass_languagesspinnerprogressmsgreused_from_previousprev_rowprev_koreanr   current_val_rawcurrent_valr   r   
translatedoutput_encodingf_outwriters#                                      r1   process_csv_filer    s-    
j#R	@ {/D%&&
CJ<PWZWaWab{/ {/  <]
-!(!4 k	/HC{77:.4";;=Kwww'-2446G#z1 B#CGG,?$@A  !- 1 1 3z)#CGG,Eh,O$PQ N  C JN+C#m2D,DEG"%a
|4HgY&A77++J788*AG  #2T2
 $) ]67(,,W5'#+<<
#;#Ar"H"H"JK"k1k(4(9(9(; CH'838V080BHC 04,#C   JN+C#m2D,DEG"%a
|4HgY&M77++J788*AG  #2T2 C JN+C#m2D,DEG"%a
|4Hyrww7G7G
7S6TTVW_V``abC#2T2)5););)= +%+:- ~-"%''("3"9r-335  #,6;";L 23*M5kB+ +# +	
 !+H9+< KK 
Q'c-.@(@A!U!J<0G9N2773C3CJ3O2PPRS[R\\]^cr.Wk	/!{/z KK,t<%.kGO	k3"	E *= )001A1A*1MNO [5{/ {/B sL   ASB?S0SCSS"E<S S'AS9SSSS!c            	         t         j                  j                  t        d      } t	        j
                  d      }|j                  ddddd	       |j                  d
dddd	       |j                  dddd       |j                  ddd       |j                         }|j                  r|j                  n| }t         j                  j                  |      }t        t         j                  j                  t        d            }t        t         j                  j                  |d            }t        |      }d}|j                  r*t         j                  j                  |j                        }n|}t         j                  j                  |      r|}t        j                  |      D 	cg c]`  }	t         j                  j                  t         j                  j                  ||	            r!|	j!                         j#                  d      r|	b }
}	|
st%        d| t&        j(                         yt%        d|        t%        d|        t%        d|        t%        d|        t%        dt+        |
       d       |
D ]!  }t         j                  j                  ||      }t         j                  j                  ||      }t         j                  j                  ||      }t%        d |        d}|j,                  s|rt/        ||      }|rt1        |      }t3        ||d!|j,                  ||j4                   "       t7        j8                  ||       	 t%        d#| d$       t;        |d%d&'      5 }t%        |j=                                ddd       t%        d(       $ nt         j                  j                  |      r|j!                         j#                  d      s3t%        d,| t&        j(                         t'        j@                  d-       t         j                  jC                  |      }t         j                  j                  ||      }t         j                  j                  ||      }t%        d.|        t%        d|        t%        d|        t%        d|        t%        d/       t%        d |        d}|j,                  s|rt/        ||      }|rt1        |      }t3        ||d!|j,                  ||j4                   "       t7        j8                  ||       	 t%        d#| d$       t;        |d%d&'      5 }t%        |j=                                ddd       t%        d(       n3t%        d0| t&        j(                         t'        j@                  d-       t%        d1       t%        d2|        t%        d3|        yc c}	w # 1 sw Y   MxY w# t>        $ r-}t%        d)| d*| d+t&        j(                         Y d}~d}~ww xY w# 1 sw Y   xY w# t>        $ r,}t%        d)| d*| d+t&        j(                         Y d}~d}~ww xY w)4u  
    --file/-f 파라미터로 파일 또는 디렉토리를 지정할 수 있습니다.
    - 지정하지 않으면 기본값: springtime-server/translate/cvs 디렉토리
    - 디렉토리이면: 내부의 모든 CSV 파일을 번역
    - 파일이면: 해당 CSV 파일만 번역
    출력은 항상 springtime-server/translate/output 아래에 생성됩니다.
    최신 번역본은 output/all 에 저장하고, 실행별 보관본은 output/YYYYMMDDHHMMSS 에 저장합니다.
    이전 번역 재사용은 기본적으로 output/all 에서 찾으며,
    --previous_output/-p 로 다른 디렉토리 또는 파일을 지정할 수 있습니다.
    cvsu*   DeepL을 사용한 CSV 번역 스크립트)descriptionz--filez-fr   uG   번역할 CSV 파일 또는 CSV 파일들이 있는 디렉토리 경로N)desthelpdefaultz--previous_outputz-pprevious_outputu   이전에 생성된 번역 CSV 들이 있는 디렉토리 또는 단일 CSV 파일 경로. 같은 파일이 있고, 같은 Key 의 한글 값이 동일하면 이전 번역 값을 그대로 재사용합니다.z--renewz-r
store_trueu   CSV 나 이전 결과에 기존 번역이 있어도 모두 무시하고, Korean(ko) 컬럼 기준으로 모든 언어를 다시 DeepL로 번역합니다.)actionr  z--no-bomu=   출력 CSV를 UTF-8 with BOM 대신 UTF-8로 저장합니다.outputallz.csvu#   [WARN] CSV 파일이 없습니다: r   u   입력 디렉토리 : u   출력 디렉토리 : u   최신본 디렉토리 : u   실행본 디렉토리 : u   처리 대상 CSV : u   개u    - 처리 중: T)r   r   r   r   u   
===== 번역 결과 보기: z =====r   r   r   u   ===== 끝 =====
u(   [WARN] 결과 파일 출력 중 오류: r   r   u$   [ERROR] CSV 파일이 아닙니다: r{   u   입력 파일     : u(   처리 대상 CSV : 1개 (단일 파일)u8   [ERROR] 파일/디렉토리를 찾을 수 없습니다: u$   모든 CSV 번역 및 저장 완료.u   최신본 디렉토리: u   실행본 디렉토리: )"r/   r*   r9   BASE_DIRargparseArgumentParseradd_argument
parse_argsr   abspathr2   r;   r   r?   listdirr@   r   endswithr   r   r   r   r   rC   r   r  no_bomshutilcopyfiler   re   r   exitrA   )default_input_dirparserargstarget_pathr4   latest_output_rootrun_output_rootr<   	input_dirr   	csv_filesr=   in_pathout_pathlatest_out_pathprev_rows_for_file	cand_prevf_resr   s                      r1   mainr?  k  s    Xu5$$1]^F
V   D   	 _   L  
 D#yy$)).?K''//+.KRWW\\(H=>K#BGGLLe$DE*;7O !%M(<(<=*	ww}}[!	 ZZ	*
ww~~bggll9a89	""6* 
	 
 7	{C#**U&yk23&{m45)*<)=>?)/):;<$S^$4C89! 	dHggll9h7Gww||OX>H ggll+=xHOOH:./ DH::-5mXN	);I)F&!jj0"kk/ OOHo6d6xjGH(C+> (%%**,'()*9	d@ 
	$  "++F38FSZZXHHQK77##K077<<:'',,'98D$[M23&{m45)*<)=>?)/):;<89z*+?Czzm1-JI%7	%B"**,++o	
 	/2	`28*FCDhk: $eejjl#$%&
 	HV]`]g]gh	
01	$%7$8
9:	$_$5
67A
V( (  d@
"QCqQX[XbXbccdL$ $  	`<XJb1MTWT^T^__	`sg   -A%WW.<W!W.6X3 X'-X3 !W+	&W..	X$7"XX$'X0,X3 3	Y(<"Y##Y(__main__)r   N)KONN   g      ?)FFNT)>r&  r   r6   r^   r/   r.  r   r   urllib.errorra   urllib.parseurllib.requesttypingr   r   r]   DEEPL_API_BASEr   r   r*   r   r*  __file__
SCRIPT_DIRPROJECT_DIRr9   r%  r   r   str__annotations__r#   r   r   r   r   r'   rL   r(   r)   r2   r;   rC   boolrI   rM   objectrl   rx   r   r   r   r   r   r   r   intfloatr   r  r?  __name__ r3   r1   <module>rS     sx    
   	  
      7 )"#=1-.n=  WW__RWW__X67
ggooj)77<<[1ww||Hj1
 










"S#X & 










' T#s(^ & 
'  Cy !%$- d38n & "  ;= 4c3S01367 <!#S#X #S S <c <c <S4Z 3 3QU: 33: 3$ 3
* * * %)%	%% sF{
d
"% 
#v+	%.' ' ' C D <*s * *3 *SVY]S] * # $ i it iX	c3h S	 	4Z	2# $sDcN/B*C : "L"
L"L" L" 4Z	L"
 tL" L" L" 	L"d 6:VPVPVP VP 	VP
 T#s(^+,t3VP VP 
VPr`8F zF r3   