U
    sÁj~#  ã                
   @  s&  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Zddl	m	Z	m
Z
 ddlmZ ddlmZ ddlmZ ddlmZmZmZ d	Zd
Zdddœdd„Zdddœdd„Zdddddœdd„Zdddddœdd„Zdddddœdd„Zdddd œd!d"„Zddd#d$œd%d&„Zdd'd(œd)d*„Zdd+œd,d-„Z e!d.k�r"ze"e ƒ ƒ‚W nÚ ek
�r† Z# z e$d/e#› �ej%d0� e"d1ƒ‚W 5 dZ#[#X Y nœ ek
�râ Z# z>d2e&e#ƒk�r¶e$d3ej%d0� ne$d4e#› �ej%d0� e"d1ƒ‚W 5 dZ#[#X Y n@ e'k
�r  Z# z e$d5e#› �ej%d0� e"d1ƒ‚W 5 dZ#[#X Y nX dS )6zÔFetch a month of seminars from seminar.zenko-sai.or.jp.

The site is an authenticated Nuxt application.  This script deliberately uses
the normal browser flow instead of hard-coding the site's private API login.
é    )ÚannotationsN)ÚdatetimeÚtimezone)ÚPath)ÚAny)ÚError)ÚPageÚTimeoutErrorÚsync_playwrightz#https://seminar.zenko-sai.or.jp/topz"https://www.zenko-sai.or.jp/login/Ústrztuple[int, int]©ÚvalueÚreturnc                 C  s6   t  d| ¡}|st d¡‚t| d¡ƒt| d¡ƒfS )Nz(\d{4})-(0[1-9]|1[0-2])zmonth must be YYYY-MMé   é   )ÚreÚ	fullmatchÚargparseZArgumentTypeErrorÚintÚgroup)r   Úmatch© r   útools/fetch_seminars.pyÚparse_month   s    
r   r   c                 C  sT   t | tƒrt dd| ¡ ¡ S t | tƒr4dd„ | D ƒS t | tƒrPdd„ |  ¡ D ƒS | S )Nz\s+ú c                 S  s   g | ]}t |ƒ‘qS r   ©Úclean)Ú.0Úitemr   r   r   Ú
<listcomp>'   s     zclean.<locals>.<listcomp>c                 S  s   i | ]\}}|t |ƒ“qS r   r   )r   Úkeyr   r   r   r   Ú
<dictcomp>)   s      zclean.<locals>.<dictcomp>)Ú
isinstancer   r   ÚsubÚstripÚlistÚdictÚitems©r   r   r   r   r   #   s    


r   r   z
str | NoneÚNone)ÚpageÚusernameÚpasswordr   c                 C  s¢   | j tdd� |  d¡}|  d¡}| ¡ r2| ¡ s:tdƒ‚|pDtdƒ}|pRt d¡}| |¡ | |¡ |  d¡j|d	� d
¡j	 
¡  |  d¡ | j tdd� d S )NÚdomcontentloaded©Z
wait_untilz#login_user_namez#login_passwordul   ãƒ­ã‚°ã‚¤ãƒ³ç”»é�¢ã�®å…¥åŠ›æ¬„ã�Œè¦‹ã�¤ã�‹ã‚Šã�¾ã�›ã‚“ã€‚ã‚µã‚¤ãƒˆå�´ã�®å¤‰æ›´ã‚’ç¢ºèª�ã�—ã�¦ã��ã� ã�•ã�„ã€‚u   å…¨åŽšæ¸ˆãƒ­ã‚°ã‚¤ãƒ³ID: u   å…¨åŽšæ¸ˆãƒ‘ã‚¹ãƒ¯ãƒ¼ãƒ‰: Zform)Zhaszbutton, input[type=submit])ÚgotoÚ	LOGIN_URLÚlocatorÚcountÚRuntimeErrorÚinputÚgetpassÚfillÚfilterÚfirstÚclickÚwait_for_load_stateÚTOP_URL)r*   r+   r,   ÚuserZpasswdr   r   r   Úlogin-   s    




r=   r   )r*   ÚyearÚmonthr   c              	     sX  |   d¡ |d›d|d›d�}|  d¡}g }t| ¡ ƒD ]Z}| |¡}| d¡pRd ¡ }| d	¡pdd ¡ ‰ |d
ksˆt‡ fdd„dD ƒƒr8| |¡ q8t	|ƒdkrÚ|d  
|¡ |d  
|d›d|d›dtddƒd›�¡ n | j|› d|› d�dd�j ¡  |  d¡}	ddg}
|
D ]:}|	j|d� ¡ �r|	j|d�j ¡  |   d¡  dS �qtdƒ‚dS )z=Fill date controls and submit the site's seminar search form.ZnetworkidleÚ04dú/Ú02dz/01r4   ÚtypeÚ Úname>   Údater?   c                 3  s   | ]}|ˆ kV  qd S ©Nr   )r   Zword©rE   r   r   Ú	<genexpr>G   s     zchoose_month.<locals>.<genexpr>)ÚfromÚtorF   r>   r?   r   r   r   é   u   å¹´u   æœˆF)ÚexactZbuttonu   æ¤œç´¢ZSearch)Zhas_textNuo   ã‚»ãƒŸãƒŠãƒ¼æ¤œç´¢ãƒœã‚¿ãƒ³ã�Œè¦‹ã�¤ã�‹ã‚Šã�¾ã�›ã‚“ã€‚ã‚µã‚¤ãƒˆå�´ã�®ç”»é�¢å¤‰æ›´ã‚’ç¢ºèª�ã�—ã�¦ã��ã� ã�•ã�„ã€‚)r:   r1   Úranger2   ZnthÚget_attributeÚlowerÚanyÚappendÚlenr6   ÚminZget_by_textr8   r9   Zget_by_roler7   r3   )r*   r>   r?   ÚtargetZinputsZdate_inputsÚir   ÚtypZbuttonsZ
candidatesZlabelr   rH   r   Úchoose_month=   s,    


, 


rX   z	list[Any]c                   s2   g ‰ |   d‡ fdd„¡ t| ||ƒ |  d¡ ˆ S )zCRead the rendered page and capture the site's seminar API payloads.Úresponsec                   s
   t | ˆ ƒS rG   )Ú_capture)rY   ©Úpayloadsr   r   Ú<lambda>]   ó    zapi_payloads.<locals>.<lambda>iÜ  )ZonrX   Zwait_for_timeout)r*   r>   r?   r   r[   r   Úapi_payloadsZ   s
    
r_   )rY   r\   r   c                 C  sR   d| j ksd| j krd S z|  ¡ }W n tk
r:   Y d S X ||krN| |¡ d S )Nz/api/Zseminar)ÚurlÚjsonÚ	ExceptionrR   )rY   r\   Údatar   r   r   rZ   c   s    rZ   zlist[dict[str, Any]])r\   r*   r   c                   s¢   g ‰ t ƒ ‰dddœ‡ ‡‡fdd„‰| D ]}ˆ|ƒ q&ˆ sž| d¡ ¡ D ]V}t d| d¡p\d	¡}|rF| d
¡ˆkrF| d
¡}ˆ |¡ ˆ  || d¡dœ¡ qFˆ S )Nr   r)   r   c                   sÄ   t ˆ tƒr¤ˆ  d¡p&ˆ  d¡p&ˆ  d¡}|d k	rŒt‡ fdd„dD ƒƒrŒt|ƒ}|ˆkrŒˆ |¡ tˆ ƒ}||d< | dd|› �¡ ˆ |¡ ˆ  	¡ D ]}ˆ|ƒ q”nt ˆ t
ƒrÀˆ D ]}ˆ|ƒ q²d S )	NÚ
seminar_idZ	seminarIdÚidc                 3  s   | ]}|ˆ kV  qd S rG   r   )r   r    r(   r   r   rI   u   s     z1extract_records.<locals>.visit.<locals>.<genexpr>)Zstart_atZstart_datetimer   Zseminar_daterF   Ú
detail_urlz(https://seminar.zenko-sai.or.jp/seminar/)r"   r&   ÚgetrQ   r   Úaddr   Ú
setdefaultrR   Úvaluesr%   )r   ZidentÚsidr   Zchild©ÚrecordsÚseenÚvisitr(   r   ro   r   s    



zextract_records.<locals>.visitza[href*="/seminar/"]z/seminar/(\d+)ZhrefrD   r   )rd   rf   )	Úsetr1   Úallr   ÚsearchrO   r   rh   rR   )r\   r*   ZpayloadÚlinkr   rk   r   rl   r   Úextract_recordsn   s    


rt   zdict[str, Any])r*   r   c                 C  sZ   g }t |  d¡ ¡ dd�D ]6\}}dd„ | d¡ ¡ D ƒ}|r| ||g dœ¡ qd|iS )	z:Preserve a lightweight copy of the visible calendar table.ztable trr   )Ústartc                 S  s   g | ]}t | ¡ ƒ‘qS r   )r   Ú
inner_text)r   Zcellr   r   r   r   –   s     z"original_table.<locals>.<listcomp>zth,td)Z	day_indexZoriginal_cellsZeventsÚdays)Ú	enumerater1   rq   rR   )r*   rw   ÚindexÚrowZcellsr   r   r   Úoriginal_table’   s    r{   )r   c               	   C  s  t jdd�} | jdtdd� | jddd� | jd	d
d� | jdddd� |  ¡ }|j\}}t|jƒ}|jddd� t|j	ƒ}t
ƒ ��†}|jj|j d�}ddi}| ¡ r´t|ƒ|d< |jf |Ž}	|	 ¡ }
|
jtdd� d|
jksô|
 d¡ ¡  ¡ �st|
t d¡t d¡ƒ d|
jk�r tdƒ‚t|
||ƒ}t||
ƒ}|�sDtdƒ‚|	j	t|ƒd� t t j!¡j"dd �}||d!œ}||d"›d#|d$›d%� j#t$j%|d&d'd(�d) d*d+� ||d"›d#|d$›d,� j#t$j%t&|
ƒd&d'd(�d) d*d+� t'd-|d"›d#|d$›d.t(|ƒ› d/�ƒ | )¡  W 5 Q R X d0S )1Nu3   å…¨åŽšæ¸ˆã‚»ãƒŸãƒŠãƒ¼ã‚’æœˆå�˜ä½�ã�§å�–å¾—ã�—ã�¾ã�™)Zdescriptionr?   u   å�–å¾—å¯¾è±¡æœˆ (YYYY-MM))rC   Úhelpz--output-dirz	docs/json)Údefaultz--storage-statez.seminar-storage-state.jsonz--headedÚ
store_trueu!   ãƒ–ãƒ©ã‚¦ã‚¶ç”»é�¢ã‚’è¡¨ç¤ºã�™ã‚‹)Úactionr|   T)ÚparentsÚexist_ok)ZheadlessZlocalezja-JPÚstorage_stater-   r.   zzenko-sai.or.jp/loginZbodyZZENKO_SAI_USERNAMEZZENKO_SAI_PASSWORDr=   uc   ãƒ­ã‚°ã‚¤ãƒ³ã�§ã��ã�¾ã�›ã‚“ã�§ã�—ã�Ÿã€‚èª�è¨¼æƒ…å ±ã�¾ã�Ÿã�¯SSOçŠ¶æ…‹ã‚’ç¢ºèª�ã�—ã�¦ã��ã� ã�•ã�„ã€‚uu   ã‚»ãƒŸãƒŠãƒ¼ã�Œå�–å¾—ã�§ã��ã�¾ã�›ã‚“ã�§ã�—ã�Ÿã€‚æ¤œç´¢çµ�æžœã�¾ã�Ÿã�¯APIãƒ¬ã‚¹ãƒ�ãƒ³ã‚¹ã‚’ç¢ºèª�ã�—ã�¦ã��ã� ã�•ã�„ã€‚)ÚpathZmilliseconds)Ztimespec)Úcaptured_atrm   r@   ú-rB   z.jsonFr   )Zensure_asciiÚindentÚ
zutf-8)Úencodingz-original-table.jsonu   å�–å¾—å®Œäº†: z / u   ä»¶r   )*r   ÚArgumentParserÚadd_argumentr   Ú
parse_argsr?   r   Ú
output_dirÚmkdirr‚   r
   ZchromiumZlaunchZheadedÚexistsr   Znew_contextZnew_pager/   r;   r`   r1   rv   r$   r=   ÚosÚgetenvr3   r_   rt   r   Znowr   ZutcZ	isoformatZ
write_textra   Údumpsr{   ÚprintrS   Úclose)ÚparserÚargsr>   r?   rŒ   ÚstateZ
playwrightZbrowserZcontext_argsÚcontextr*   r\   rm   r„   Úresultr   r   r   Úmainœ   sP    





 ÿ ÿ$r™   Ú__main__u   ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆ: )Úfiler   zExecutable doesn't existut   ãƒ–ãƒ©ã‚¦ã‚¶ã�Œæœªã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã�§ã�™ã€‚`python3 -m playwright install chromium` ã‚’å®Ÿè¡Œã�—ã�¦ã��ã� ã�•ã�„ã€‚u   Playwrightã‚¨ãƒ©ãƒ¼: u   ã‚¨ãƒ©ãƒ¼: )(Ú__doc__Z
__future__r   r   r5   ra   r�   r   Úsysr   r   Úpathlibr   Útypingr   Zplaywright.sync_apir   ZPlaywrightErrorr   r	   ZPlaywrightTimeoutErrorr
   r;   r0   r   r   r=   rX   r_   rZ   rt   r{   r™   Ú__name__Ú
SystemExitÚexcr’   Ústderrr   r3   r   r   r   r   Ú<module>   sJ   
	$
*
