o
    J‚žjj:  ã                   @   s¦  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
 eej dd¡ƒZej ej e¡¡Zej edd¡Zej edd¡Zej edd¡Zej edd	¡Ze ¡ Zd
d„ Zdd„ Zdd„ Zd$dd„Zd%dd„Zdd„ Z dd„ Z!G dd„ dej"j#ƒZ$G dd„ dej%ej&ƒZ'e(dkrÑedƒ ej)e!d d!Z*e* +¡  e'defe$ƒZ,e-d"e› ƒ e-d#e› ƒ e, .¡  W d  ƒ dS 1 sÊw   Y  dS dS )&é    N)ÚdatetimeÚ	timedeltaÚPORTi#  Údatazequipment.jsonzmembers.jsonzhistory.jsonÚbackupsc                 C   sŽ   | rt | tƒs	d S z3t ¡ }t |  ¡ d¡}|j}|jdkr(|jdkr(|d7 }n|jdkr6|jdkr6|d8 }|j|dW S  t	yF   Y d S w )Nú%m/%dé   é   )Úyear)
Ú
isinstanceÚstrr   ÚnowÚstrptimeÚstripr
   ÚmonthÚreplaceÚ
ValueError)Zdate_strr   Údtr
   © r   úHC:\Users\djw7ql\OneDrive - Aptiv\Antigravity\KR_Eng_Lab_Status\server.pyÚparse_dt   s   
ÿr   c              
   C   s¦   t j | ¡}z3tjd|d\}}t j|ddd}tj||ddd W d	  ƒ n1 s,w   Y  t  || ¡ W d	S  t	yR } zt j 
|¡rLt  |¡ |‚d	}~ww )
z”Write JSON data to a file safely using atomic write (temp file + rename).
    This prevents file corruption if the process is interrupted mid-write.z.tmp)ÚsuffixÚdirÚwúutf-8©ÚencodingFé   )Úensure_asciiZindentN)ÚosÚpathÚdirnameÚtempfileÚmkstempÚfdopenÚjsonÚdumpr   Ú	ExceptionÚexistsÚremove)Úfilepathr   Zdir_nameÚfdZtmp_pathZtmp_fÚer   r   r   Úatomic_write_json-   s   ÿ
€ür-   c              	   C   sª   t I g }tj t¡r0zttddd}t |¡}W d  ƒ n1 s#w   Y  W n   g }Y t 	¡  
d¡| d< | | ¡ tt|ƒ W d  ƒ dS 1 sNw   Y  dS )z*Append a completed project to history.jsonÚrr   r   Nz%Y-%m-%d %H:%M:%SZarchived_at)Ú	file_lockr   r    r(   ÚHISTORY_PATHÚopenr%   Úloadr   r   ÚstrftimeÚappendr-   )Zproject_dataÚhistoryÚfr   r   r   Úrecord_history>   s   ÿ€
"ôr7   Ú c              
   C   s¸   t j t¡sdS t jtdd t ¡  d¡}| rd| › nd}d|› |› d}t j 	t|¡}zt
 t|¡ td	|› ƒ tƒ  W dS  ty[ } ztd
|› ƒ W Y d}~dS d}~ww )z-Create a timestamped backup of equipment.jsonNT)Úexist_okz%Y%m%d_%H%M%SÚ_r8   Ú
equipment_ú.jsonz[Backup] Created: z[Backup] Failed: )r   r    r(   ÚEQUIPMENT_PATHÚmakedirsÚ
BACKUP_DIRr   r   r3   ÚjoinÚshutilÚcopy2ÚprintÚcleanup_old_backupsr'   )ZlabelÚ	timestampr   Zbackup_nameZbackup_pathr,   r   r   r   Úcreate_backupS   s   €ÿrF   é   c                 C   st   t j t¡sdS tdd„ t  t¡D ƒƒ}t|ƒ| kr8| d¡}t  t j 	t|¡¡ t
d|› ƒ t|ƒ| ksdS dS )z?Keep only the most recent N backup files to prevent disk bloat.Nc                 S   s$   g | ]}|  d ¡r| d¡r|‘qS )r;   r<   )Ú
startswithÚendswith)Ú.0r6   r   r   r   Ú
<listcomp>h   s    
ÿÿÿz'cleanup_old_backups.<locals>.<listcomp>r   z[Backup] Cleaned old: )r   r    r(   r?   ÚsortedÚlistdirÚlenÚpopr)   r@   rC   )Zmax_backupsr   Zoldestr   r   r   rD   d   s   ÿ
ýrD   c                  C   s€  t 2 z-tj t¡sW W d  ƒ dS ttddd} t | ¡}W d  ƒ n1 s+w   Y  W n  tyQ } zt	d|› ƒ W Y d}~W d  ƒ dS d}~ww t
 ¡ }d}|D ]’}| ddd	d	d	d	d	d
œ¡}| ddd	d	d	d	d	d
œ¡}t| d¡ƒ}t| d¡ƒ}	t| d¡ƒ}
|	rß| ¡ |	 ¡ krßd| dd	¡ ¡ vr¿t|d |d | dd¡|d |d |d |d |d dœƒ | ¡ |d< dddd|	tdd  d¡d	d
œ|d< d}|d }nn|rM| ¡ | ¡ k rM| d¡dkrM| d¡ d¡sM| d¡r| d¡ d¡r| ¡ |d< t| di ¡ d¡ƒ}|r)|tdd  d¡n| d¡}dddd||tdd  d¡d
œ|d< d}|d }| d|¡}| d¡rz|
rz| ¡ |
 ¡ krz| ¡ |d< dd	d	d	d	d	d
œ|d< d}|d }|d }d}| dd	¡}| p‹d| ¡ v }|s½t| d¡ƒ}t| d¡ƒ}|rº|rº| ¡ | ¡   krµ| ¡ krºn nd }nd!}n| d¡rÅd!}| d"¡|krÓ||d"< d}|rí|dkrí| d¡dkríd|d< d|d< d}qZ|rztt|ƒ t	d#| d$¡› ƒ W n" ty } zt	d%|› ƒ W Y d}~nd}~ww W d  ƒ dS W d  ƒ dS W d  ƒ dS 1 s9w   Y  dS )&zRun the schedule shift & status automation on equipment data.
    This is called by the background thread, NOT by GET requests.Nr.   r   r   z"[Automation] Failed to read data: FÚcurrentZETCr8   )ÚtypeÚprojectÚitemÚengineerÚstartÚendÚnextrU   rV   zwaiting for testrR   ÚidÚnamerQ   rS   rT   )Zeq_idZeq_namerQ   rR   rS   rT   rU   rV   ZpreviouszSTAND BY: waiting for testú-r	   )Údaysr   TzSTOP: waiting for testz	STAND BY:ZSTOPZRUNzSTAND BYÚstatusz[Automation] Data updated at z%H:%M:%Sz[Automation] Failed to save: )r/   r   r    r(   r=   r1   r%   r2   r'   rC   r   r   Ú
setdefaultr   ÚgetÚdateÚlowerr7   Úcopyr   r3   rH   r-   )r6   r   r,   ÚtodayÚmodifiedÚeqÚcurrZnxtZ
c_start_dtZc_end_dtZ
n_start_dtZprev_end_dtZw_startZtarget_statusZc_projZis_idleZs_dtZe_dtr   r   r   Úrun_automation_logicu   s¬   ýÿ€
ø€
ý&
"$
þ$4€
€ÿü©Z¦\$¤rf   c               
   C   sx   d} t dƒ 	 ztƒ  t ¡  ¡ }| |krtdƒ |} W n ty5 } zt d|› ƒ W Y d}~nd}~ww t d¡ q)zQBackground thread: runs automation every 60 seconds and daily backup at midnight.Nz6[Thread] Background automation started (interval: 60s)TZdailyz[Thread] Automation error: é<   )	rC   rf   r   r   r_   rF   r'   ÚtimeÚsleep)Zlast_backup_dateZ
today_dater,   r   r   r   Úautomation_thread_worker×   s    €€ÿ
òrj   c                       s<   e Zd Z‡ fdd„Z‡ fdd„Z‡ fdd„Zdd„ Z‡  ZS )	Ú
APIHandlerc                    s   t ƒ j|dti|¤Ž d S )NZ	directory)ÚsuperÚ__init__Ú	DIRECTORY)ÚselfÚargsÚkwargs©Ú	__class__r   r   rm   ò   s   zAPIHandler.__init__c                    s&   |   dd¡ |   dd¡ tƒ  ¡  d S )NzCache-Controlz.no-store, no-cache, must-revalidate, max-age=0ZPragmazno-cache)Úsend_headerrl   Úend_headers)ro   rr   r   r   ru   õ   s   zAPIHandler.end_headersc              
      s  | j  d¡rqzNt$ ttddd}t |¡}W d   ƒ n1 s!w   Y  W d   ƒ n1 s0w   Y  |  d¡ |  dd¡ |  	¡  | j
 tj|dd	 d¡¡ W d S  typ } z|  d
d|› ¡ W Y d }~d S d }~ww d| j v røz^g }t2 tj  t¡r¦ttddd}| ¡  ¡ }|r—t |¡}W d   ƒ n1 s¡w   Y  W d   ƒ n1 s°w   Y  |  d¡ |  dd¡ |  	¡  | j
 tj|dd	 d¡¡ W d S  ty÷ } ztd|› ƒ |  d
d|› ¡ W Y d }~d S d }~ww | j  d¡r}zYg }t, tj  t¡r)ttddd}t |¡}W d   ƒ n	1 s$w   Y  W d   ƒ n	1 s4w   Y  |  d¡ |  dd¡ |  	¡  | j
 tj|dd	 d¡¡ W d S  ty| } ztd|› ƒ |  d
d|› ¡ W Y d }~d S d }~ww tƒ  ¡  d S )Nú	/api/datar.   r   r   éÈ   úContent-typeúapplication/jsonF)r   éô  zError reading data: z/api/historyzHistory Fetch Error: zError reading history: ú/api/memberszMembers Fetch Error: zError reading members: )r    rH   r/   r1   r=   r%   r2   Úsend_responsert   ru   ÚwfileÚwriteÚdumpsÚencoder'   Ú
send_errorr   r(   r0   Úreadr   ÚloadsrC   ÚMEMBERS_PATHrl   Údo_GET)ro   r6   r   r,   Úcontentrr   r   r   r…   ú   st   ÿ€ÿ
" €ÿ

€ý€þ
" €þÿ€þ
" €þzAPIHandler.do_GETc              
   C   s  | j dkr~zLt| jd ƒ}| j |¡}t | d¡¡}t t	t
|ƒ W d   ƒ n1 s-w   Y  |  d¡ |  dd¡ |  ¡  | j t ddi¡ d¡¡ W d S  ty} } z |  d	¡ |  ¡  | j t d
t|ƒi¡ d¡¡ W Y d }~d S d }~ww | j dkrüzLt| jd ƒ}| j |¡}t | d¡¡}t t	t|ƒ W d   ƒ n1 s«w   Y  |  d¡ |  dd¡ |  ¡  | j t ddi¡ d¡¡ W d S  tyû } z |  d	¡ |  ¡  | j t d
t|ƒi¡ d¡¡ W Y d }~d S d }~ww |  dd¡ d S )Nrv   zContent-Lengthr   rw   rx   ry   r\   Zsuccessrz   Úerrorr{   i”  z	Not Found)r    ÚintÚheadersZrfiler‚   r%   rƒ   Údecoder/   r-   r=   r|   rt   ru   r}   r~   r   r€   r'   r   r„   r   )ro   Úcontent_lengthZ	post_dataZ	json_datar,   r   r   r   Údo_POST+  sJ   
ÿ
"
.€ý
ÿ
"
.€ýzAPIHandler.do_POST)Ú__name__Ú
__module__Ú__qualname__rm   ru   r…   rŒ   Ú__classcell__r   r   rr   r   rk   ñ   s
    1rk   c                   @   s   e Zd ZdZdZdS )ÚReusableTCPServerTN)r   rŽ   r   Zallow_reuse_addressZdaemon_threadsr   r   r   r   r‘   V  s    r‘   Ú__main__ZstartupT)ÚtargetÚdaemonzServing at http://localhost:zBackups stored in: )r8   )rG   )/Zhttp.serverÚhttpZsocketserverr%   r   r"   rA   Ú	threadingrh   r   r   rˆ   Úenvironr^   r   r    r!   ÚabspathÚ__file__rn   r@   r=   r„   r0   r?   ÚLockr/   r   r-   r7   rF   rD   rf   rj   ÚserverZSimpleHTTPRequestHandlerrk   ZThreadingMixInZ	TCPServerr‘   r   ÚThreadZ
automationrU   ÚhttpdrC   Úserve_foreverr   r   r   r   Ú<module>   sF    

be
"ý÷