
    ^ph                        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	Z	ddl
Z
ddlZddlZddlZddlmZ ddlmZ 	 ddlmZ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 dd
lmZ 	 ddl m!Z! 	 ddl"Z"ddl"m#Z$  G d d      Z%e&dk(  r	  e%       Z'e'jQ                          yy# e$ r  ed       dZdZY vw xY w# e$ r  ed       dZY w xY w# e$ r  ed       dZY w xY w# e$ r  ed	       dZY w xY w# e$ r  ed       dZdZdZY w xY w# e$ r  ed       dZ!Y w xY w# e$ r  ed       dZ"dZ$Y w xY w)u   
GeoMedical Helper Client - 자동 업데이트 클라이언트
이 프로그램은 서버에서 코드를 다운로드하고 실시간으로 업데이트합니다.
main.py만 수정하여 새로운 기능을 배포할 수 있습니다.
    N)datetime)urlparse)ImageImageTkz2PIL not installed. Image features will be limited.z8keyboard not installed. Hotkey features will be limited.z=pyautogui not installed. Automation features will be limited.zApywin32 not installed. Windows-specific features will be limited.)ttkz4tkinter not available. GUI features will be limited.)get_monitorsz4screeninfo not installed. Using default screen size.)MenuItemz<pystray not installed. System tray features will be limited.c                   0   e Zd ZdZd Zd Zd Zd Zd0dZd Z	d	 Z
d1d
Zd2dZd0dZd Zd Zd Zd Zd Zd Zd Zd Zd3dZd Zd Zd Zd Zd Zd Zd4dZd0dZd Zd Z d  Z!d! Z"d" Z#d# Z$d$ Z%d% Z&d& Z'd' Z(d( Z)d) Z*d* Z+d+ Z,d1d,Z-d1d-Z.d. Z/d/ Z0y)5GeoMedicalClientus   
    메인 클라이언트 클래스
    서버와 통신하여 코드를 다운로드하고 실행합니다.
    c                    d| _         d| _        d| _        d| _        d| _        d| _        d| _        d| _        | j                  | j
                  | j                  | j                  fD ]  }t        j                  |d	
        d | _
        d | _        g | _        g | _        i | _        d | _        t!               | _        | j%                          | j'                          | j)                          d| _        d | _        d	| _        y )Nz6https://file.geomedical.kr/file/GEOmedical/update/helpzlocal_version.jsonzcached_main.pyzapp_data.json	downloadscachemodulespluginsT)exist_okF)
server_urllocal_version_filelocal_code_fileapp_data_filedownloads_dir	cache_dirmodules_dirplugins_dirosmakedirscurrent_modulegui_rootoverlay_windowsbackground_threadshotkeysactive_popupsetprocessed_requestscheck_dependenciesload_pluginssync_configmanagement_check_running	tray_icon
is_running)selfdir_paths     //mnt/c/Users/Administrator/Desktop/help_/app.py__init__zGeoMedicalClient.__init__S   s    R #7/, ) $$ ++T^^T=M=MtO_O_` 	1HKK40	1 #!"$ "%% 	! ).%     c                     t         j                  j                  | j                        r5t	        | j                  d      5 }t        j                  |      cddd       S dddS # 1 sw Y   xY w)u5   로컬에 저장된 버전 정보를 불러옵니다.rNz0.0.0 )versionhash)r   pathexistsr   openjsonloadr*   fs     r,   get_local_versionz"GeoMedicalClient.get_local_version}   sZ    77>>$112d--s3 $qyy|$ $"B//$ $s    A$$A-c                     t        | j                  d      5 }t        j                  ||d       ddd       y# 1 sw Y   yxY w)u+   버전 정보를 로컬에 저장합니다.w   )indentN)r6   r   r7   dump)r*   version_datar:   s      r,   save_local_versionz#GeoMedicalClient.save_local_version   s9    $))3/ 	11IIlAa0	1 	1 	1s	   9Ac                 t   i }t         j                  j                  | j                        r6t	        | j                  dd      5 }t        j                  |      }ddd       |||<   t	        | j                  dd      5 }t        j                  ||dd       ddd       y# 1 sw Y   IxY w# 1 sw Y   yxY w)	u   
        app_data.json에 데이터를 저장합니다.
        key: 저장할 데이터의 키
        value: 저장할 값 (JSON 직렬화 가능해야 함)
        r0   utf-8encodingNr=   r>   Fr?   ensure_asciir   r4   r5   r   r6   r7   r8   r@   )r*   keyvaluedatar:   s        r,   	save_datazGeoMedicalClient.save_data   s     77>>$,,-d((#@ $Ayy|$S	$$$cG< 	=IIdAae<	= 	=$ $	= 	=s   B"?B."B+.B7Nc                    t         j                  j                  | j                        rIt	        | j                  dd      5 }t        j                  |      }|j                  ||      cddd       S |S # 1 sw Y   |S xY w)u   
        app_data.json에서 데이터를 불러옵니다.
        key: 불러올 데이터의 키
        default: 키가 없을 때 반환할 기본값
        r0   rD   rE   N)r   r4   r5   r   r6   r7   r8   get)r*   rJ   defaultr:   rL   s        r,   	load_datazGeoMedicalClient.load_data   sl     77>>$,,-d((#@ .Ayy|xxW-. . . s   'A55A?c                     t         j                  j                  | j                        r7t	        | j                  dd      5 }t        j                  |      cddd       S i S # 1 sw Y   i S xY w)u.   저장된 모든 데이터를 반환합니다.r0   rD   rE   N)r   r4   r5   r   r6   r7   r8   r9   s     r,   get_all_datazGeoMedicalClient.get_all_data   sV    77>>$,,-d((#@ $Ayy|$ $	$	s   A##A-c                 x   t         j                  j                  | j                        ryt	        | j                  dd      5 }t        j                  |      }ddd       |v r>||= t	        | j                  dd      5 }t        j                  ||dd       ddd       yyy# 1 sw Y   MxY w# 1 sw Y   yxY w)	u+   특정 키의 데이터를 삭제합니다.r0   rD   rE   Nr=   r>   FrG   rI   )r*   rJ   r:   rL   s       r,   delete_datazGeoMedicalClient.delete_data   s    77>>$,,-d((#@ $Ayy|$d{I$,,cGD EIIdAaeDE E  .$ $E Es   B$?B0$B-0B9c                    	 |s]t         j                  j                  t        |      j                        }|s)dt	        t        t        j                                     z   }t         j                  j                  | j                  |      }t        j                  |d      }t        |j                  j                  dd            }d}t        |d      5 }|j                  d      D ]6  }	|	s|j                  |	       |t        |	      z  }|s(|dkD  s. |||       8 	 d	d	d	       |S # 1 sw Y   |S xY w# t         $ r}
t#        d
|
        Y d	}
~
y	d	}
~
ww xY w)u   
        파일을 다운로드합니다.
        url: 다운로드할 파일의 URL
        filename: 저장할 파일명 (None이면 URL에서 추출)
        progress_callback: 진행상황 콜백 함수 (downloaded, total)
        	download_T)streamzcontent-lengthr   wbi    )
chunk_sizeNDownload error: )r   r4   basenamer   strinttimejoinr   requestsrO   headersr6   iter_contentwritelen	Exceptionprint)r*   urlfilenameprogress_callbackfilepathresponse
total_size
downloadedr:   chunkes              r,   download_filezGeoMedicalClient.download_file   s/   	77++HSM,>,>?*STYY[1A-BBHww||D$6$6AH||C5HX--112BAFGJJh% F%22d2C FE"c%j0
,a-j*EFF OF O 	$QC()	sH   CD9 D,.!D,D,D,"	D9 ,D61D9 6D9 9	EEEc                    	 t        j                  |j                               j                         dz   }t        j
                  j                  | j                  |      }|r!t        j
                  j                  |      r|S t        j                  |d      }|j                  dk(  r2t        |d      5 }|j                  |j                         ddd       |S 	 y# 1 sw Y   |S xY w# t        $ r}t!        d|        Y d}~yd}~ww xY w)u   
        이미지를 다운로드하고 캐싱합니다.
        url: 이미지 URL
        cache: 캐시 사용 여부
        z.jpg
   timeout   rY   NzImage download error: )hashlibmd5encode	hexdigestr   r4   r`   r   r5   ra   rO   status_coder6   rd   contentrf   rg   )r*   rh   r   cache_filename
cache_pathrl   r:   rp   s           r,   download_imagezGeoMedicalClient.download_image   s    	0$[[6@@BVKNdnnnEJ 
3!!  ||C4H##s**d+ .qGGH,,-.!! + .!! 	0*1#.//	0s<   B C* 2C* 5C	C* C'"C* 'C* *	D3DDc                     t         r1	 |rt        j                  |      }|S t        j                         }|S y# t        $ r}t        d|        Y d}~yd}~ww xY w)uj   
        화면을 캡처합니다.
        region: 캡처할 영역 (x, y, width, height) 튜플
        )regionzScreenshot error: N)	pyautogui
screenshotrf   rg   )r*   r   r   rp   s       r,   capture_screenzGeoMedicalClient.capture_screen   sj    
 0!*!5!5V!DJ "! "+!5!5!7J!!   0*1#.//0s   9 9 	AAAc                     t         r'	 t        j                  ||       || j                  |<   yy# t        $ r}t	        d|        Y d}~yd}~ww xY w)u   
        글로벌 핫키를 등록합니다.
        key_combination: 키 조합 (예: 'ctrl+space')
        callback: 핫키가 눌렸을 때 실행할 함수
        TzHotkey registration error: NF)keyboard
add_hotkeyr    rf   rg   )r*   key_combinationcallbackrp   s       r,   register_hotkeyz GeoMedicalClient.register_hotkey  sZ     9##OX>08_-   93A37889s   %/ 	AAAc                     t         r2|| j                  v r$	 t        j                  |       | j                  |= yy# t        $ r}t	        d|        Y d}~yd}~ww xY w)u$   등록된 핫키를 해제합니다.TzHotkey removal error: NF)r   r    remove_hotkeyrf   rg   )r*   r   rp   s      r,   unregister_hotkeyz"GeoMedicalClient.unregister_hotkey  s_    4<<74&&7LL1   4.qc2334s   ": 	AAAc                 `   ddl } |j                         }|j                  dd       |j                  dd       |j                  d       |j	                  |j                          d|j                          d       |j                  d	
       t        r}|j                         }t        j                  |t        j                        }|t        j                  z  t        j                  z  }t        j                  |t        j                  |       | j                   j#                  |       |S )u   
        투명한 전체화면 오버레이 윈도우를 생성합니다.
        파이 메뉴 등에 사용할 수 있습니다.
        r   Nz-alphagffffff?-topmostTxz+0+0blackbg)tkinterToplevel
attributesoverrideredirectgeometrywinfo_screenwidthwinfo_screenheight	configurewin32guiwinfo_idGetWindowLongwin32conGWL_EXSTYLEWS_EX_LAYEREDWS_EX_TRANSPARENTSetWindowLongr   append)r*   tkoverlayhwndstyless        r,   create_overlay_windowz&GeoMedicalClient.create_overlay_window!  s    
 	"++-8S):t,  & 	G5578':T:T:V9WW[\] 	W% ##%D++D(2F2FGFh444x7Q7QQF""4)=)=vF##G,r.   c                     t        j                  |||d      }|j                          | j                  j	                  |       |S )u   
        함수를 백그라운드 스레드에서 실행합니다.
        func: 실행할 함수
        args, kwargs: 함수에 전달할 인자
        T)targetargskwargsdaemon)	threadingThreadstartr   r   )r*   funcr   r   threads        r,   run_in_backgroundz"GeoMedicalClient.run_in_background?  s=     !!DPTU&&v.r.   c                     	 ddi}d|i}t        j                  |||      }|j                  dk(  S # t        $ r}t	        d|        Y d}~yd}~ww xY w)	u   
        카카오워크 웹훅으로 메시지를 전송합니다.
        webhook_url: 카카오워크 웹훅 URL
        message: 전송할 메시지
        Content-Typeapplication/jsontext)r7   rb   rv   zKakaoWork message error: NF)ra   postr{   rf   rg   )r*   webhook_urlmessagerb   rL   rl   rp   s          r,   send_kakaowork_messagez'GeoMedicalClient.send_kakaowork_messageL  sb    	%'9:GG$D}}[tWMH''3.. 	-aS12	s   .1 	AAAc                    	 | j                    d| d}t        j                  |d      }|j                  dk(  rat        j
                  j                  | j                  | d      }t        |dd      5 }|j                  |j                         d	d	d	       |S 	 y	# 1 sw Y   |S xY w# t        $ r}t        d
|        Y d	}~y	d	}~ww xY w)u4   서버에서 추가 모듈을 다운로드합니다.z	/modules/.pyrs   rt   rv   r=   rD   rE   NzModule download error: )r   ra   rO   r{   r   r4   r`   r   r6   rd   r   rf   rg   )r*   module_name
module_urlrl   module_pathr:   rp   s          r,   download_additional_modulez+GeoMedicalClient.download_additional_module]  s    		1 OO,Ik]#FJ||J;H##s* ggll4+;+;}C=PQ+sW= +GGHMM*+""	 + +"" 	1+A3/00	1s6   A3B* 5B	B* B'"B* 'B* *	C3CCc                    t         j                  j                  | j                  | d      }t         j                  j	                  |      s| j                  |      }|r|t         j                  j	                  |      r]	 t        j                  j                  ||      }t        j                  j                  |      }|j                  j                  |       |S y# t        $ r}t        d|        Y d}~yd}~ww xY w)u!   추가 모듈을 로드합니다.r   Module load error: N)r   r4   r`   r   r5   r   	importlibutilspec_from_file_locationmodule_from_specloaderexec_modulerf   rg   )r*   r   r   specmodulerp   s         r,   load_additional_modulez'GeoMedicalClient.load_additional_modulek  s    ggll4#3#3}C5HIww~~k*99+FK277>>+61 ~~==k;W"88>''/   1+A3/001s    AC 	C>&C99C>c                 N   t        d      D ]K  }	 t        j                  | j                   ddddi      }|j                  dk(  r|j                         c S M y	# t        $ r@}|dk  rt        j                  d|z         Y d	}~zt        d
|dz    d|        Y d	}~d	}~ww xY w)uV   서버에서 최신 버전 정보를 확인합니다. (400대 대응 재시도 로직)   z/version.json   
Connectionclose)ru   rb   rv   r>   NzVersion check error after    z attempts: )
rangera   rO   r   r{   r7   rf   r_   sleeprg   )r*   attemptrl   rp   s       r,   check_updatezGeoMedicalClient.check_update}  s    Qx 	PG
P#<<4??*;=(I.0/;W.EG ''3.#==?* /	P   PQ;JJqG|,27Q;-{1#NOO	Ps   AA	B$$BBB$c                     	 t        j                  | j                   d| d      }|j                  dk(  r|j                  S 	 y# t
        $ r}t        d|        Y d}~yd}~ww xY w)u-   서버에서 코드를 다운로드합니다./rs   rt   rv   r[   N)ra   rO   r   r{   r   rf   rg   )r*   ri   rl   rp   s       r,   download_codezGeoMedicalClient.download_code  sq    	*||t&7q
$CRPH##s*}}$ +   	*$QC())	*s   A A 	A&A!!A&c                     t        | j                  dd      5 }|j                  |       ddd       y# 1 sw Y   yxY w)u$   코드를 로컬에 저장합니다.r=   rD   rE   N)r6   r   rd   )r*   coder:   s      r,   save_local_codez GeoMedicalClient.save_local_code  s6    $&&g> 	!GGDM	 	 	s   4=c                     t         j                  j                  | j                        r2t	        | j                  dd      5 }|j                         cddd       S y# 1 sw Y   yxY w)u.   로컬에 저장된 코드를 불러옵니다.r0   rD   rE   N)r   r4   r5   r   r6   readr9   s     r,   load_local_codez GeoMedicalClient.load_local_code  sP    77>>$../d**C'B  avvx    s   AA&c                 d    t        j                  |j                               j                         S )u+   코드의 SHA256 해시를 계산합니다.)rw   sha256ry   rz   )r*   r   s     r,   calculate_hashzGeoMedicalClient.calculate_hash  s     ~~dkkm,6688r.   c                 `   ddddd}g }|j                         D ]  \  }}	 t        |        |rM| j	                  dd      r:t        d|        	 t        j                  t        j                  d	d
dg|z          yyy# t        $ r |j                  |       Y w xY w#  t        d       Y yxY w)uK   필요한 라이브러리를 확인하고 자동 설치를 시도합니다.PILr   r   r   )pillowr   r   pywin32auto_install_depsFzInstalling missing libraries: z-mpipinstallz,Failed to install dependencies automaticallyN)
items
__import__ImportErrorr   rQ   rg   
subprocess
check_callsys
executable)r*   requiredmissingpackageimport_names        r,   r$   z#GeoMedicalClient.check_dependencies  s     "$!	
 $,NN$4 	( G[(;'	( t~~&95A27)<=F%%s~~tUI&NQX&XY B7  (w'(FDEs   A>*B >BBB-c                    t         j                  j                  | j                        rnt        j                  | j                        D ]K  }|j                  d      s|j                  d      r'|dd }	 | j                  |       t        d|        M yy# t        $ r}t        d| d|        Y d}~od}~ww xY w)u?   plugins 디렉토리의 모든 플러그인을 로드합니다.r   _NzLoaded plugin: zFailed to load plugin : )
r   r4   r5   r   listdirendswith
startswithr   rg   rf   )r*   ri   plugin_namerp   s       r,   r%   zGeoMedicalClient.load_plugins  s    77>>$**+JJt'7'78 K$$U+H4G4G4L"*3B-KK33K@}=>K , % K 6{m2aSIJJKs   6B	B="B88B=c                     	 t        j                  | j                   dd      }|j                  dk(  r$|j	                         }| j                  d|       |S 	 | j                  di       S #  Y xY w)u.   서버의 설정 파일과 동기화합니다.z/config.json   rt   rv   server_config)ra   rO   r   r{   r7   rM   rQ   )r*   rl   configs      r,   r&   zGeoMedicalClient.sync_config  sq    	||t&7|$DaPH##s*!7 + ~~or22	s   AA, ,A0c                    	 t        j                         j                         | j                         d   t	        |      t        j                         |d}| j                  dg       }|j                  |       | j                  d|dd        | j                  dd      r'	 t        j                  | j                   d|d	
       yy#  Y yxY w#  Y yxY w)u   
        에러를 로컬에 기록하고 선택적으로 서버에 전송합니다.
        error: 발생한 에러
        context: 에러가 발생한 상황 설명
        r2   )	timestampr2   error	tracebackcontext	error_logiNsend_error_reportsFz/error_reportr   r7   ru   )r   now	isoformatr;   r]   r  
format_excrQ   r   rM   ra   r   r   )r*   r  r  
error_datar  s        r,   report_errorzGeoMedicalClient.report_error  s    	%\\^557113I>U&113"J {B7IZ(NN;	$%(89 ~~2E:MMT__$5]"C*^_` ;	s$   BC !%C C
C C Cc                 $   	 ddl }|j                  j                  dd      }|||||d}t        j                  d|ddd	i
      }|j
                  dk(  ryt        d|j
                          y# t        $ r}	t        d|	        Y d}	~	yd}	~	ww xY w)u   
        서버로 로그를 전송합니다.
        level: 'ERROR', 'WARNING', 'INFO', 'DEBUG'
        log_type: 'SYSTEM', 'NETWORK', 'UPDATE', 'GUI', 'CUSTOM'
        message: 로그 메시지
        details: 추가 상세 정보 (선택적)
        r   NCOMPUTERNAMEUnknown)hostnameleveltyper   detailsz#http://localhost:5000/api/agent/logr   r   r   )r7   ru   rb   rv   TzLog send failed: FzLog send error: )r   environrO   ra   r   r{   rg   rf   )
r*   r  log_typer   r  r   r  log_datarl   rp   s
             r,   send_logzGeoMedicalClient.send_log  s    	zz~~ni@H % ""H  }}5');<	H ##s*)(*>*>)?@A 	$QC()	s   AA. A. .	B7B

Bc                 `	    t         j                  j                  dd      }t         j                  j                  |      } fd|_         fd|_        d |_         fd|_         fd|_         fd	|_	         j                  |_
         j                  |_         j                  |_         j                  |_         j                  |_         j                  |_         j                   |_         j"                  |_         j$                  |_         j&                  |_         j(                  |_         j*                  |_         j,                  |_         j.                  |_         j0                  |_         j2                  |_        t4        |_        t6        |_        t8        |_        t:        |_        t<        |_        t>        |_        t@        |_         tB        |_!        tD        |_"        tF        |_#        tI        d
      |_%        tI        d      |_&        tI        d      |_'        tP        rtP        |_(        tR        |_)        tT        rtT        |_*        tV        rtV        |_+        tX        r!tX        |_,        tZ        |_-        t\        |_.        	 t_        ||j`                         |S # tb        $ r}|je                  d      }|jf                  r|jf                  dz
  nd}ti        dd        ti        d|jf                          ti        d|jj                          tm        d|dz
        }to        tq        |      |dz         }ti        d|jf                   d       ti        d       ts        ||      D ]r  }	|	dz   }
|	tq        |      k  r||	   nd}|	|k(  r?ti        d|
dd|        |jt                  sAti        dd|jt                  dz
  z   d       ati        d|
dd|        t ti        d       t6        jv                  jy                   jz                  d       }t}        |d!d"#      5 }|j                  |       ddd       n# 1 sw Y   nxY wti        d$|         d}~wt        $ r>}ti        d%t        |      j                   d&|        t        j                           d}~ww xY w)'u   
        다운로드한 코드를 모듈로 로드하고 필요한 함수들을 주입합니다.
        main.py에서 사용할 수 있는 모든 기능이 여기서 정의됩니다.
        dynamic_mainN)r   c                       j                   S N)r   r*   s   r,   <lambda>z.GeoMedicalClient.load_module.<locals>.<lambda>)  s    $-- r.   c                     t        d|       S )Nr   )setattr)rootr*   s    r,   r  z.GeoMedicalClient.load_module.<locals>.<lambda>*  s    wtZ'F r.   c                      t         j                  j                  t         j                  j                  t                    S r  )r   r4   dirnameabspath__file__ r.   r,   r  z.GeoMedicalClient.load_module.<locals>.<lambda>+  s     bggoobggooh6O&P r.   c                  *     j                         d   S )Nr2   )r;   r  s   r,   r  z.GeoMedicalClient.load_module.<locals>.<lambda>,  s    T%;%;%=i%H r.   c                       j                   S r  )r   r  s   r,   r  z.GeoMedicalClient.load_module.<locals>.<lambda>-  s    4+=+= r.   c                       j                   S r  )r   r  s   r,   r  z.GeoMedicalClient.load_module.<locals>.<lambda>.  s    t~~ r.   mathrandomre
r   r   z<============================================================zSyntax Error at line zError: rs      z
Code around line :z<------------------------------------------------------------r1   z>>> 5dz | z	       |  ^z    zerror_code.pyr=   rD   rE   zError code saved to: zRuntime error in module: r   )Er   r   spec_from_loaderr   get_rootset_rootget_app_pathget_versionget_downloads_dirget_cache_dirrM   rQ   rS   rU   rq   r   r   r   r   r   r   r   r   r  r&   r  r7   r   r_   r   r   ra   base64ior   rw   r   r+  r,  r-  r   r   r   r   r   r   win32apiexec__dict__SyntaxErrorsplitlinenorg   msgmaxminre   r   offsetr4   r`   r   r6   rd   rf   r  __name__r  	print_exc)r*   r   r   r   rp   lines
error_liner   endiline_numline_content
debug_filer:   s   `             r,   load_modulezGeoMedicalClient.load_module   s   
 ~~..~d.K006 0FPH#= 5  >>>>"//!--  $11 $ 3 3 !% 3 3 "&!5!5#'#9#9  (,'A'A$ $(#9#9  )-(C(C% )-(C(C% #//!---- 	"$"	&  ("8,t$	  FL$FN&FO(F&FO&FO&FO)	v'T S  #	JJt$E)*AqJBvh- )!((45GAEE7#$ :?+Ec%j*r/2C'z34(O5#& 	Aq5+,s5z>uQxr
?D"S?@xx	#A*>)?qABD"S?@	A (O dnnoFJj#8 A  )*67 	-d1g.>.>-?r!EF!	sD   J( (
R-2DQ#6A7Q#-Q?	Q#Q	Q##R-/9R((R-c                      y)uD   서버에 연결할 수 없을 때 사용할 기본 코드입니다.a  
import tkinter as tk
from datetime import datetime

def main():
    root = tk.Tk()
    set_root(root)
    root.title("GEOmedical Helper - Offline")
    root.geometry("400x300")
    
    tk.Label(root, text="GEOmedical Support Tool", font=("Arial", 16)).pack(pady=20)
    tk.Label(root, text="Running in offline mode", fg="red").pack(pady=10)
    tk.Label(root, text=f"Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}").pack(pady=10)
    tk.Button(root, text="Close", command=root.quit).pack(pady=20)
    
    root.mainloop()
r'  r  s    r,   create_offline_codez$GeoMedicalClient.create_offline_code  s    r.   c                    t        d| j                          | j                         }| j                         }d}|rt        d|d           t        d|d           |d   |d   kD  rrt        d       | j	                         }|rU| j                  |      }||d<   | j                  |       | j                  |       t        d|d           nt        d	       |s>| j                         }|s,t        d
       | j                         }| j                  |       	 | j                  |      | _        y# t        $ r>}t        d|        t        j                          | j                  |d       Y d}~yd}~ww xY w)u>   업데이트를 확인하고 최신 코드를 실행합니다.zConnecting to: NzServer version: r2   zLocal version: zDownloading update...r3   zUpdated to version z)Server not reachable, using local versionz*No local code found, using offline mode...Tr   zModule loadingF)rg   r   r;   r   r   r   r   rB   r   rR  rP  r   rf   r  rH  r  )r*   local_versionremote_versionr   	code_hashrp   s         r,   update_and_runzGeoMedicalClient.update_and_run  s|   012..0**,$^I%>$?@AOM)$<#=>?i(=+CC-.))+ $ 3 3D 9I-6N6*((.++N;/y0I/JKL=> '')DBC//1$$T*	"&"2"24"8D 	's+,!a!12		s   D4 4	E;=4E66E;c                    | j                   rt        d       	 | j                   j                          | j                   j                          d| _         | j                  D ]  }	 |j                           | j                  j                          | j                         }|rI	 | j                  |      | _        t        | j                  d      r| j                  j                          yyyy#  Y xY w#  Y xY w# t        $ r>}t        d|        t        j                          | j                  |d       Y d}~yd}~ww xY w)u<   GUI를 재시작합니다. 업데이트 후 호출됩니다.zRestarting GUI...NmainzGUI restart error: zGUI restart)r   rg   quitdestroyr   clearr   rP  r   hasattrrY  rf   r  rH  r  )r*   r   r   rp   s       r,   restart_guizGeoMedicalClient.restart_gui  s)   ==%&""$%%' !DM  // OO%
   &&( '')D8*.*:*:4*@D't22F;++002 < ' 
 ! 8/s34'')%%a778s0   4C- $C4#AC; -C14C8;	E4D==Ec                    	 t         j                  j                  dd      }t        d|        t	        j                  d| d      }t        d|j
                          |j
                  dk(  r|j                         }t        d	|        |j                  d
      r|j                  d      rt        dt        |d          d       |d   D ]  }t        d|d    d|d           |d   | j                  vrh| j                  It        d|d           | j                  |       | j                  ddd|d    d|d    d|d           t        d|d    d       t        d|d    d        yt        d       yt        d|j
                          | j                  ddd |j
                          y# t        $ r7}t        d!|        | j                  d"dd!t        |              Y d}~yd}~ww xY w)#u>   서버에서 소프트웨어 관리 요청을 확인합니다.r  r  z)[DEBUG] Checking management requests for 7http://localhost:5000/api/software/management-requests/r   rt   z[DEBUG] Request response: rv   z[DEBUG] Response data: successra   z[DEBUG] Found z	 requestsz[DEBUG] Processing request ID: idz, Software: software_nameNz$[DEBUG] Creating popup for request: INFOSYSTEMzManagement request received: zRequest ID: z
, Action: action_typez[DEBUG] Skipping request z - popup already activez - already processedz![DEBUG] No pending requests foundz0[WARNING] Management request check failed: HTTP WARNINGz&Management request check failed: HTTP z Management request check error: ERROR)r   r  rO   rg   ra   r{   r7   re   r#   r!   handle_management_requestr  rf   r]   )r*   r  rl   rL   reqrp   s         r,   check_for_management_requestsz.GeoMedicalClient.check_for_management_requests  sY   #	Zzz~~ni@H=hZHI||&]^f]g$hrstH.x/C/C.DEF##s*}}/v6788I&488J+?N3tJ/?+@*AKL#J/ _ ?D	{,WZ[jWkVlmn t9D,C,CC#008 %(LSQUYK&X Y $ > >s C $fhB_`cds`t_u@v  {G  HK  LP  HQ  GR  R\  ]`  an  ]o  \p  yq  !r %(A#d)Lc&d e!$=c$i[H\"]^_ =?HI]I]H^_`i5[\d\p\p[q3rs 	Z4QC89MM'8/OPSTUPVx-XYY	Zs$   E7F? :F? 8F? ?	G?-G::G?c                     | j                   ryd| _         t        j                  | j                  d      }|j	                          t        d       | j                  dddd       y)	u6   관리 요청 확인을 위한 별도 스레드 시작NTr   r   u8   [INFO] Management request checker started (30초 간격)rd  re  z"Management request checker startedzCheck interval: 30 seconds)r'   r   r   _management_request_loopr   rg   r  )r*   management_threads     r,    start_management_request_checkerz1GeoMedicalClient.start_management_request_checker"  sY    (((,%%,,D4Q4QZ^_!HIfh(LNjkr.   c           	         | j                   r	 d}	 | j                  r0t        | j                  d      r| j                  j                         }|r| j	                          nt        d       t        j                  d	       | j                   ryy#  d}Y HxY w# t        $ r7}t        d|        | j                  dddt        |              Y d}~hd}~ww xY w)
u)   관리 요청 확인 루프 (30초마다)Fwinfo_existsz9[DEBUG] GUI not active, skipping management request checkz'[ERROR] Management request loop error: rh  re  zManagement request loop error: N   )r'   r   r]  rr  rk  rg   rf   r  r]   r_   r   )r*   
gui_existsrp   s      r,   rn  z)GeoMedicalClient._management_request_loop-  s    ++]"
'}})O%)]]%?%?%A
 668UV JJrN) ++'!&J  ]?sCDgx3RSVWXSYRZ1[\\]s.   B <B B BB 	C!-CCc                 <    t         st        d| d|        y j                  (	  j                  j                         rt        d       y	 	 t        r&t	               d   }|j
                  }|j                  }nd}d}t        j                         j                  d       j                  d	d       j                  d
        _        t        j                  d      }	|	j                  dddd       t        j                  d
dd      }
|
j                  dddd       d}d}||z
  dz
  }||z
  dz
  }j                  | d| d| d|        t        j                  |
|dd
dd      }|j                  ddd       t        j                  |
d
      }|j                  ddd d!       t        j                  ||d"d
d#d$d%d&d'(	      }|j                  d d)        fd* fd+}j!                  d,|       j#                  d-fd.       |
j#                  d-fd/       |	j#                  d-fd0       |j#                  d-fd1       |j#                  d-fd2       |j#                  d-fd3       |}t%        d|dz   d4      D ]H  }||z
  }j                  | d| d| d|        j'                          t)        j*                  d5       J y#  d _        Y xY w# t,        $ r}t        d6|        d _        Y d}~yd}~ww xY w)7u'   카카오스타일 팝업 알림 생성z$GUI not available for notification: z - Nz(Popup already active, skipping new popupr     8  Tr   z#ffffffr   z#ccccccr>   r   )r   yrelwidth	relheightsolid)r   reliefbdi|  rv      <   r   +)   맑은 고딕   boldz#333333r=   )r   fontr   fganchorr   )r   r   )padxpadyfillr   both)r  r  r  expand)r  rs   z#666666nwleftiT     )r   r  r   r  r  justify
wraplengthheight)r  r  c                      j                   j                         d _        j                           dk(  rj	                  d       y  dk(  rj	                  d       y y )Ndeletion_request	completedforce_removal)r#   addr!   r[  report_management_completion)rf  popup
request_idr*   s   r,   on_closez<GeoMedicalClient.create_popup_notification.<locals>.on_close  s^    ''++J7$(!"4455j+N O355j+N 4r.   c                      d  _         y r  )r!   r  s   r,   
on_destroyz>GeoMedicalClient.create_popup_notification.<locals>.on_destroy  s    $(!r.   WM_DELETE_WINDOW
<Button-1>c                             S r  r'  rp   r  s    r,   r  z<GeoMedicalClient.create_popup_notification.<locals>.<lambda>  s	    xz r.   c                             S r  r'  r  s    r,   r  z<GeoMedicalClient.create_popup_notification.<locals>.<lambda>  s	    HJ r.   c                             S r  r'  r  s    r,   r  z<GeoMedicalClient.create_popup_notification.<locals>.<lambda>  s	    hj r.   c                             S r  r'  r  s    r,   r  z<GeoMedicalClient.create_popup_notification.<locals>.<lambda>  s	    8: r.   c                             S r  r'  r  s    r,   r  z<GeoMedicalClient.create_popup_notification.<locals>.<lambda>  s	    xz r.   c                             S r  r'  r  s    r,   r  z<GeoMedicalClient.create_popup_notification.<locals>.<lambda>  s	    XZ r.      {Gz?zPopup creation error: )r   rg   r!   rr  r   widthr  r   r   r   r   Frameplacer   Labelpackprotocolbindr   updater_   r   rf   )r*   titler   rf  r  rc  screenscreen_widthscreen_heightshadow_frame
main_framer  r  r   rx  title_labelmessage_containermessage_labelr  start_ystep	current_yrp   r  r  s   `  ``                  @@r,   create_popup_notificationz*GeoMedicalClient.create_popup_notificationE  s4   8s7)LM ()$$113DE 4e	%%*%|| &  $ $ KKME""4(Z.OOyO) !&D 88Ei8La!qA %Ig!LJqAQ? EF u$r)A&+ANNeWAfXQqc1#67 ((2K "7= !#	 B"""M HH!*
M F48O) NN-z: JJ|%9:OOL*>?l,@A""<1EF|-AB\+?@ $Ga"a0 !#dN	%&1#QykBC

4 	!C)$(!J  	%*1#./ $D	%s$   %K$ JK3 $	K03	L<LLc           
      n   t         sy	 t        r&t               d   }|j                  }|j                  }nd}d}t        j                         j                  d       j                  dd       j                  |       d}d	}||z
  d
z
  }	||z
  dz
  }
j                  | d| d|	 d|
        t        j                  |d|d      j                  d       t        j                  |d|dd      j                  d       fdj                  dfd       j                  d       |}t        d|dz   d      D ]H  }||z
  }j                  | d| d|	 d|        j                          t        j                   d       J y# t"        $ r}t%        d|        Y d}~yd}~ww xY w)u   결과 알림 팝업Nr   rv  rw  Tr   r   i,  P   r~  r  r   r  )r  r/  r  white)r   r  r   r  )rs   r>   )r  )r  	   i  )r   r  r   r  r  )r   rs   c                  &     j                          y r  )r[  )result_popups   r,   close_resultz?GeoMedicalClient.show_result_notification.<locals>.close_result  s    $$&r.   r  c                             S r  r'  )rp   r  s    r,   r  z;GeoMedicalClient.show_result_notification.<locals>.<lambda>  s	    ln r.   i  rs   r  zResult notification error: )r   r   r  r  r   r   r   r   r   r  r  r  afterr   r  r_   r   rf   rg   )r*   r  r   colorr  r  r  r  r  r   rx  r  r  r  rp   r  r  s                  @@r,   show_result_notificationz)GeoMedicalClient.show_result_notification  s   ;	5%*%|| &# $;;=L))$/##J5""e",EFu$r)A&+A!!UG1VHAaS!"=> HH2 dd  HH) dd ' l,DE t\2 $Ga"b1 !#dN	%%q!Ai[&IJ##%

4 	!  	5/s344	5s   FF 	F4F//F4c                 8   t         st        d|        y	 |d   }|d   }|d   }|dk(  r	d}d| d	}n|d
k(  rd}d| d	}| j                  |||       y# t        $ r9}t        d|        | j	                  |j                  d      d       Y d}~yd}~ww xY w)u!   관리 요청을 처리합니다.zGUI not available for request: Nrf  rc  rb  r  u%   🗑️ 소프트웨어 삭제 알림uK   관리자가 다음 소프트웨어의 삭제를 요청했습니다:

📦 u#   

이 알림을 확인했습니다.r  u+   ⚠️ 소프트웨어 강제 제거 알림uL   관리자에 의해 다음 소프트웨어가 강제 제거됩니다:

📦 z#Management request handling error: failed)r   rg   r  rf   r  rO   )r*   requestrf  rc  r  r  r   rp   s           r,   ri  z*GeoMedicalClient.handle_management_request  s    3G9=>	K!-0K#O4M J00?ijwix  y^  _/Ejkxjy  z_  ` **5';
Tab 	K7s;<--gkk$.?JJ	Ks   ?A 	B /BBc                     	 t        j                  d| dd|id      }|j                  dk(  rt        d| d|        y
y
# t        $ r}t        d	|        Y d
}~y
d
}~ww xY w)u(   관리 요청 완료를 서버에 보고r`  z	/completeresultr   r	  rv   zManagement request z completed with status: z(Failed to report management completion: N)ra   r   r{   rg   rf   )r*   r  statusrl   rp   s        r,   r  z-GeoMedicalClient.report_management_completion  s    		B}}I*U^_'H
 ##s*+J<7OPVxXY + 	B<QC@AA	Bs   >A 	A#AA#c                 0   	 t        j                  d       	 d}	 | j                  r0t        | j                  d      r| j                  j	                         }|r)t        dt        j                         j                  d              | j                         }| j                         }|r|d   |d   kD  rt        d|d    d|d           | j                         }|r| j                  |       | j                  |       t        d	       | j                  |      }t        |d
      r|j                          	 | j                  r<t        | j                  d      r&| j                  j!                  d| j"                         nt        d       y#  d}Y AxY w# t$        $ r}t        d|        Y d}~(d}~ww xY w# t$        $ rM}dt'        |      vr6dt'        |      vr)t        d|        	 | j)                  |d       n#  Y nxY wY d}~d}~ww xY w)u0   주기적으로 업데이트를 확인합니다.i  Frr  u%   
[자동 업데이트 확인 중...] z%Y-%m-%d %H:%M:%Sr2   z
[Update Available] z -> z'[Update Complete] Hot-reload successful	on_updater  d   z[GUI Restart] Error: Nz8[Update Check] GUI not available, stopping update checkszinvalid command namezmain thread is not in main loopz[Update Check] Error: zUpdate check)r_   r   r   r]  rr  rg   r   r
  strftimer   r;   r   r   rB   rP  r  r  r^  rf   r]   r  )r*   rt  rU  rT  r   
new_modulegui_erp   s           r,   check_updates_periodicallyz+GeoMedicalClient.check_updates_periodically'  s   JJt-"
'}})O%)]]%?%?%A
 B8<<>CZCZ[nCoBpqr%)%6%6%8N$($:$:$<M%.*CmT]F^*^ 5mI6N5OtTbclTmSnop#113 006 33NC!"KL *.)9)9$)?J&z;? * 4 4 6G#'==WT]]G5T$(MM$7$7T=M=M$N
 TUO '!&J2 $- G %(=eW&E F FG  )Q7<]ehijek<k21#67))!^<sr   F? <F C"F? :AF F? FF? 	F<$F72F? 7F<<F? ?	H(H1HHHHHc                     t        | j                  j                               D ]  }| j                  |        | j                  D ]  }	 |j                           t        d       y#  Y &xY w)u9   프로그램 종료 시 정리 작업을 수행합니다.zCleanup completedN)listr    keysr   r   r[  rg   )r*   rJ   r   s      r,   cleanupzGeoMedicalClient.cleanupZ  sn     ))+, 	(C""3'	( ++ 	G!	 	!"s   
A((A,c                    t         st        d       y	 t        r[t        j                  ddd      }ddlm} |j                  |      }|j                  g dd	d
d       	 |j                  ddd       nd}t        j                  t        d| j                        t        d| j                              }t        j                  d|d|      }|S #  Y \xY w# t        $ r}t        d|        Y d}~yd}~ww xY w)u1   시스템 트레이 아이콘을 생성합니다.z1System tray not available (pystray not installed)NRGBA)@   r  )r   r   r   r   r   )	ImageDraw)r  r  8   r  )F            )2   r     r  r>   )r  outliner  )r~     GM)r  r  r  r  )r     버전 정보u   프로그램 종료zGeoMedical HelperGeoMedical Helper ClientzFailed to create tray icon: )pystrayrg   r   newr   r  Drawellipser   Menuitemshow_version_infoquit_applicationIconrf   )r*   imager  drawmenuiconrp   s          r,   create_tray_iconz!GeoMedicalClient.create_tray_iconk  s    EF%			&(LA) ~~e,^2EObjklIIh3GIH
  <<_d&<&<=*D,A,ABD <<#*	D K)*  	045	s1   AC  C 4AC CC 	C7C22C7c           	         	 | j                         }	 t        ddd      5 }t        j                  |      }ddd       |j	                  dd      }j	                  dd	      }|j	                  d
d      }d| d| d| d| j
                   d	}	t        rQt        rKt        j                         }
|
j                          t        j                  d|	       |
j                          yt        d       t        |	       t        d       y# 1 sw Y   xY w#  i }Y xY w# t        $ r}t        d|        Y d}~yd}~ww xY w)u!   버전 정보를 표시합니다.zversion.jsonr0   rD   rE   Nr2   z1.0.0descriptionr  release_dater  u"   GeoMedical Helper Client

버전: u	   
설명: u   
릴리즈 날짜: u	   
서버: u   

상태: 실행 중r  z3
==================================================z3==================================================
zFailed to show version info: )r;   r6   r7   r8   rO   r   r   
messageboxTkwithdrawshowinfor[  rg   rf   )r*   r  r  version_infor:   rA   r2   r  r  r   r"  rp   s               r,   r  z"GeoMedicalClient.show_version_info  s8   $	7113L".#@ 0A#'99Q<L0
 #&&y':G&**=:TUK'++NIFL			 		 . !		 G juuw##OW= m$gm$90 0"!6  	71!566	7sJ   D D DD B%D %!D DD DD 	D=%D88D=c                    t        d       	 d| _        | j                  r| j                  j                          | j                  r5	 | j                  j                          | j                  j                          | j                          d| _        t        j                  d       y#  Y 1xY w# t        $ r-}t        d|        t        j                  d       Y d}~yd}~ww xY w)u    프로그램을 종료합니다.z"Shutting down GeoMedical Helper...Fr   zError during shutdown: r   N)rg   r'   r(   stopr   rZ  r[  r  r)   r   _exitrf   )r*   r  r  rp   s       r,   r  z!GeoMedicalClient.quit_application  s    23	,1D) ~~##% }}MM&&(MM))+
 LLN $DO HHQK  	+A3/0HHQKK	s/   9B/ 4B( ;,B/ (B,*B/ /	C%8#C  C%c                 X   t         sy	 | j                         | _        | j                  rRt        d       t	        j
                  | j                  j                  d      }|j                          t        d       yt        d       y# t        $ r}t        d|        Y d}~yd}~ww xY w)u1   시스템 트레이 아이콘을 시작합니다.NzStarting system tray icon...Trm  zSystem tray icon startedzFailed to create tray iconzFailed to start tray icon: )	r  r  r(   rg   r   r   runr   rf   )r*   tray_threadrp   s      r,   start_tray_iconz GeoMedicalClient.start_tray_icon  s    	5!224DN~~45'..dnn6H6HQUV!!#0123 	5/s344	5s   A2B <B 	B)B$$B)c                 d   t        d       t        d| j                         d           t        d| j                          t        st        d       t        st        d       t
        st        d       t        st        d       t        st        d	       | j                         r| j                          t        j                  | j                  d
      }|j                          | j                          	 t        | j                   d      r| j                   j#                          nt        d       | j-                          yt        d       y# t$        $ r>}t        d|        t'        j(                          | j+                  |d       Y d}~_d}~ww xY w# | j-                          w xY w)u   메인 실행 함수입니다.z"=== GeoMedical Client Starting ===z	Version: r2   zServer: z6TIP: pip install pystray pillow - for system tray iconz.TIP: pip install pillow - for image processingz.TIP: pip install keyboard - for global hotkeysz+TIP: pip install pyautogui - for automationz/TIP: pip install pywin32 - for Windows featuresTrm  rY  z'Error: No main function found in modulezRuntime error: zMain executionNzFailed to start application)rg   r;   r   r  r   r   r   r   rW  r  r   r   r  r   rp  r]  r   rY  rf   r  rH  r  r  )r*   update_threadrp   s      r,   r  zGeoMedicalClient.run  sY   23	$0029=>?@)*+ JKBCBC?@CD   " &,,D4S4S\`aM! 1134..7'',,.CD /0  7s+,##%!!!%5667 s*   :<E 	F4FF FF F/r  )NN)T)zmain.py)r1   )1rG  
__module____qualname____doc__r-   r;   rB   rM   rQ   rS   rU   rq   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r$   r%   r&   r  r  rP  rR  rW  r^  rk  rp  rn  r  r  ri  r  r  r  r  r  r  r  r  r'  r.   r,   r   r   M   s    
&T01=
E@6$	<	"$ 
9F.
K
3:'Vzx,(T8>%ZN	l0u%n@5DK8B1f#"+Z&7PB5$+1r.   r   __main__))r
  ra   r7   r   r   importlib.utilr   r_   r   rw   r  r   r;  r<  r   urllib.parser   r   r   r   r   rg   r   r   r   r   r=  r   r   tkinter.messageboxr  r   
screeninfor   r  r	   r  r   rG  clientr  r'  r.   r,   <module>r     s     	 
        	  !"


+'(Q1 Q1h& z F
JJL Q(  	
>?EG  	
DEH  	
IJI  	
MNH  	
@A	BJ
C	  	
@AL  	
HIGDs~    B+ 	C C C,  D 1D 8
D/ +B?>B?CCC)(C),C>=C>DDD,+D,/EE