pidZddlmZmZmZmZmZmZmZm Z m Z m Z m Z m Z mZmZmZddlmZmZmZmZddlmZmZddlmZddlmZddlmZddlmZdd l m!Z!dd l"m#Z#Gd d eZ$Gd deZ%dS)z6 Versions Widget for GUI - Manages project versioning )QWidget QVBoxLayout QHBoxLayout QPushButton QTableWidgetQTableWidgetItemQLabel QMessageBox QGroupBox QLineEdit QHeaderViewQAbstractItemView QFileDialogQProgressDialog QApplication)Qt pyqtSignalQThreadr)QFontQColor)logger)datetime)Path)Database)SchedulerConfig)VersionManagercpeZdZdZeeeeeZeeZ eeZ fdZ dZ xZ S) VersionWorkerz;Worker thread for version operations with progress trackingc rt||_||_||_dSN)super__init__operation_typeversion_managerkwargs)selfr#r$r% __class__s *C:\PYTHON\Scheduler\gui\versions_widget.pyr"zVersionWorker.__init__s4 ,. c fd}|jd<jdkrjjd ij}n]jdkrjjd ij}n:jdkrjjd ij}nt djj|d S#t$rQ}tj djd|j t|Yd }~d Sd }~wwxYw) z*Execute the operation in background threadcBj||||dSr )progress_updatedemit)phasecurrenttotalfilenamer&s r(progress_callbackz,VersionWorker.run..progress_callback's&%**5'5(KKKKKr)r2uploaddownloadrestorezUnknown operation type: zWorker error in z: N)r%r#r$upload_versiondownload_versionrestore_version ValueErroroperation_finishedr- Exceptionrerroroperation_failedstr)r&r2resultes` r(runzVersionWorker.run#sh / L L L L L0ADK+ ,"h..<-<KKt{KK$ 22>->MMMM$ 11=-=LL LL !QDr"rB __classcell__r's@r(rrsEEvc3S11vc{{ ///////r)rceZdZdZeZdedeffd ZdZ dZ dZ dZ d Z d Zd Zd Zd ZdZdZdZdZdZdZdZdZdZdZdZdZdZdZxZ S)VersionsWidgetz$Widget for managing project versionsdbconfigct||_||_t ||_d|_||dSr ) r!r"rNrOrr$selected_project_id _setup_uirefresh)r&rNrOr's r(r"zVersionsWidget.__init__Cs`  -b11#'   r)ct|}|dddd|dt}t d}t }|d|d||| || td|_ |j j |j| |j td|_|jj |j|jd| |jtd|_|jj |j|jd| |jtd |_|jj |j| |j||t1d }t|}t3|_|jd |jgd |jt>j j!|j"d|j#tHj%j&|j'tHj(j)|j*tHj+j,|jj-|j.|jj/|j|j0d d|j0dd|j0dd|j0dd|j0dd| |j| |dt1d|_1t|j1}t}| t dte|_3|j34d|j35d| |j3| t dte|_6|j64d| |j6td|_7|j7j |j8|j79d| |j7|||j1:d| |j1t1d} t| } t} | td|_;|j;j |j<|j;d| |j;td |_=|j=j |j>|j=d|j=9d!| |j=td"|_?|j?j |j@|j?d| |j?| | t3|_A|jAd#|jAgd$|jAt>j j!|jA"d|jA#tHj%j&|jA'tHj(j)|jA*tHj+j,|jAj-|jB|jA0d d|jA0dd%|jA0dd&|jA0dd|jA0dd|jA0d d'| |jA| | dd(S))zSetup versions UI componentsu📦 Project VersioningTu➕ New Projectu✏ Edit ProjectFu🗑 Delete Projectu 🔄 RefreshProjects)IDNamez Source PathzServer Base PathEnabledr2P)stretchzUpload New Versionz Version Name:z e.g., v1.2.0z Description:zOptional description of changesu⬆ Upload & Create VersionzIbackground-color: #4CAF50; color: white; font-weight: bold; padding: 8px;zVersion Historyu ⬇ Downloadu ↩ Restorez;background-color: #FF9800; color: white; font-weight: bold;u 🗑 Delete)rYVersion DescriptionCreatedFilesSizexi,dN)CrsetContentsMargins setSpacingrr r setPointSizesetBoldsetFont addWidget addStretchrnew_project_btnclickedconnect_create_projectedit_project_btn _edit_project setEnableddelete_project_btn_delete_project refresh_btnrS addLayoutr rprojects_tablesetColumnCountsetHorizontalHeaderLabelshorizontalHeadersetSectionResizeModer ResizeMode InteractivesetStretchLastSectionsetSelectionBehaviorrSelectionBehavior SelectRowssetSelectionMode SelectionModeSingleSelectionsetEditTriggers EditTriggerNoEditTriggersitemSelectionChanged_on_project_selected doubleClickedsetColumnWidth upload_groupr version_name_editsetPlaceholderTextsetMaximumWidthversion_desc_edit upload_btn_upload_version setStyleSheet setVisible download_btn_download_version restore_btn_restore_versiondelete_version_btn_delete_versionversions_table_on_version_selected) r&layout header_layout title_label title_fontprojects_groupprojects_layout upload_layout form_layoutversions_groupversions_layoutversion_actions_layouts r(rRzVersionsWidget._setup_uiMsT""!!"b"b111"$ 677 WW ###4   J''' ,,,  """ ++<== $,,T-ABBB 4555 +,> ? ? %--d.@AAA ((/// 5666"-.C"D"D '//0DEEE **5111 7888&~66  ((666 0111'''#:..%n55*nn **1--- 557 7 7     ,,..CCKDZDfggg ,,..DDTJJJ 001B1T1_``` ,,->-L-\]]] ++,=,I,XYYY 0889RSSS )11$2DEEE **1b111 **1c222 **1c222 **1c222 **1b111!!$"5666333&&:;;#D$566 !mm f_55666!* 11.AAA ..s333d4555f^44555!* 112STTTd4555%&CDD ''(<=== %%&qrrrdo... ,,, $$U+++*+++##455%n55"-))+++'77 !))$*@AAA $$U+++(():;;;&}55  (()>??? ##E*** &&'deee(()9:::"-m"<"< '//0DEEE **5111(()@AAA!!"8999*nn **1--- 557 7 7     ,,..CCKDZDfggg ,,..DDTJJJ 001B1T1_``` ,,->-L-\]]] ++,=,I,XYYY 0889RSSS **1b111 **1c222 **1c222 **1c222 **1b111 **1c222!!$"566633333r)ch||jr|dSdS)zRefresh all dataN)_load_projectsrQ_load_versions)r&s r(rSzVersionsWidget.refreshsA   # "    ! ! ! ! ! " "r)c |j}|jt |t |D]\}}t t|d}|tj j |j |d|t |d}|j |d|t |d}|j |d|t |d}|j |d||d rd nd }t |} | tj j |d r#| td n"| td |j |d| dS#t$r;} t!jd| t%j|dd| Yd} ~ dSd} ~ wwxYw)zLoad projects from databaseidrnamer] source_pathr_server_base_pathraenabledu✓ Yesu✗ NogreenredrbzError loading projects: ErrorzFailed to load projects: N)rNget_all_project_configsr setRowCountlen enumeraterr?setTextAlignmentr AlignmentFlag AlignCentersetItem setForegroundrr<rr=r critical) r&projectsrowprojectid_item name_item source_item server_item enabled_text enabled_itemrAs r(rzVersionsWidget._load_projectssH# Qw6688H   + +CMM : : : )( 3 3 B B W*3wt}+=+=>>(()9)EFFF#++CG<<<-WV_== #++CI>>>/w}/EFF #++CK@@@/w7I/JKK #++CK@@@-4I,>LyyH / == --b.>.JKKK9%> ..vg???? ..ve}}===#++CLAAAA5 B B8 Q Q Q L7A77 8 8 8  w0OA0O0O P P P P P P P P P QsG4G88 H=0H88H=c|js|jddS |j|j}|jt |t |D]\}}tt|d}| tj j |j |d|t|d}|j |d|t|dpd}|j |d||d }|rQ tj|}|d } n(#t |d kr |dd n|} YnxYwd } t| } |j |d | tt|d} | tj j |j |d| |ddz } t| dd} | tj j |j |d| dS#t"$r;}t%jd|t)j|dd|Yd}~dSd}~wwxYw)z"Load versions for selected projectrNr version_namer] descriptionr_ created_atz%Y-%m-%d %H:%M:%SzN/Ara files_countrb size_bytes.2fz MBrXzError loading versions: rzFailed to load versions: )rQrrrNget_project_versionsrrrr?rrrrrr fromisoformatstrftimer<rr=r r)r&versionsrversionr version_item desc_item created_timedttime_str created_item files_itemsize_mb size_itemrAs r(rzVersionsWidget._load_versionss'    + +A . . . F- Qw33D4LMMH   + +CMM : : : )( 3 3$ ?$ ? W*3wt}+=+=>>(()9)EFFF#++CG<<< 00GHH #++CLAAA-W]-C-IrJJ #++CI>>> '|4 %a%3LAA#%;;/B#C#Ca8;L8I8IB8N8N<#4#4T`$H/99 #++CLAAA.c'-2H.I.IJJ ++B,<,HIII#++CJ???",/;?,-@-@-@-@AA **2+;+GHHH#++CI>>>>I$ ?$ ?L Q Q Q L7A77 8 8 8  w0OA0O0O P P P P P P P P P Qs1D+J)E;:J;!FC4J K0KKcR|j}t|dk}|j||j||r|d}t|j|d |_ |j d| dSd|_ |j d|jddS)zHandle project selectionrTNF)r selectedItemsrrxrzr{rrHitemtextrQrrrrr)r& selected_rows has_selectionrs r(rz#VersionsWidget._on_project_selected;s+99;; M**Q.  ((777 **=999  /"&&((C'*4+>+C+CC+K+K+P+P+R+R'S'SD $   ( ( . . .    ! ! ! ! !'+D $   ( ( / / /   + +A . . . . .r)ct|jdk}|j||j||j|dS)zHandle version selectionrN)rrrrrzrr)r&rs r(rz#VersionsWidget._on_version_selectedMsnD/==??@@1D  $$]333 ##M222 **=99999r)c|j}|sdS|d}t|j|dS)zGet selected version IDNr)rrrrHrr)r&selectedrs r(_get_selected_version_idz'VersionsWidget._get_selected_version_idTsc&4466 4qkoo4&++C3388::;;;r)c ddlm}ddlm}|||j}||jjkrCtj d| |j dSdS)z Create new project configurationrProjectConfigDialogQDialogzNew project config createdN) gui.project_config_dialogrPyQt6.QtWidgetsrrNexec DialogCodeAcceptedrinforSproject_changedr-r&rrdialogs r(rwzVersionsWidget._create_project\sAAAAAA++++++$$T4733 ;;==G.7 7 7 K4 5 5 5 LLNNN  % % ' ' ' ' ' 8 7r)cfddlm}ddlm}|jst j|dddS|||j|j}||j j krLtj d|jd| |jdSdS) z#Edit selected project configurationrrrz No SelectionzPlease select a project to editNzProject config z updated)rrrrrQr warningrNrrrrrrSrr-rs r(ryzVersionsWidget._edit_projectgsAAAAAA++++++'   n6W X X X F$$T47D4LMM ;;==G.7 7 7 KL$*BLLL M M M LLNNN  % % ' ' ' ' ' 8 7r)c |jsdS|j|j}|sdS|j|j}t |}t j|dd|dd|dt jjt jj z}|t jjkr |j |jtj d|jd|_| |jdS#t $r;}tjd|t j|d d |Yd}~dSd}~wwxYwdS) z%Delete selected project configurationNzDelete Projectz)Are you sure you want to delete project 'rz'? This will also delete zI version(s). Note: ZIP files on server will NOT be deleted automatically.zDeleted project config zError deleting project: rzFailed to delete project: )rQrNget_project_configrrr questionStandardButtonYesNodelete_project_configrrrSrr-r<r=r)r&rr version_countreplyrAs r(r|zVersionsWidget._delete_projectvs'  F',,T-EFF  F7//0HIIH $   L L L%2 L L L  & *[-G-J J    K.2 2 2 V--d.FGGG Pd6NPPQQQ+/( $))+++++ V V V ;;;<<<$T74TQR4T4TUUUUUUUUU V 3 2s6A/D'' E,10E''E,c T|jsdS|j}|j}|s1t j|dd|jdS|j |j}|sdSt j |dd|d|ddt j j t j j z}|t j j krdStd dd d ||_|jd |jt$jj|jd |jd |jd |jt3d|j|j|||_|jj|j|jj|j |jj!|j"||_#|d|_$tKj&d|d|j'dS)z*Upload new version using background threadNzValidation ErrorzVersion name is requiredzUpload VersionzUpload new version 'z' for project 'rz'? This will: 1. Create ZIP snapshot from source (~250 MB with Python embedded) 2. Extract to server active location 3. Save version metadata Continue?zInitializing...rrlzUploading VersionFr3) project_idrrz$Starting upload worker for version '')(rQrrstriprr rsetFocusrNrrrrrrupload_progresssetWindowTitlesetWindowModalityrWindowModality WindowModalsetMinimumDuration setAutoClose setAutoResetshowrr$ upload_workerr,rv_on_upload_progressr;_on_upload_finishedr>_on_upload_failed_upload_version_name_upload_project_namerrstart)r&rrrr s r(rzVersionsWidget._upload_versions'  F-2244::<< ,113399;;    &8:T U U U  " + + - - - F',,T-EFF  F$   <       & *[-G-J J    K.2 2 2 F /   s     ++,?@@@ ..r/@/LMMM //222 ))%000 ))%000 !!###+   /%#     +33D4LMMM -55d6NOOO +33D4JKKK%1!$+FO!  J<JJJKKK   """""r)c ft|dsdS|dkrd}d}|dkr%|jdd|d|d}n|dkrt||z d znd}|jdd |j||} t | d kr d | d dz} |d|d |d|d| d|d }n2|dkrd}d}|dkrt||z d znd}|jdd |j||} t | d kr d | d dz} |d|d |d|d| d|d }n|dkrd}d}|dkrt||z d znd}|jdd |j||} t | d kr d | d dz} |d|d |d|d| d|d }n d}d}|d|d }|j|tjdS)z*Handle progress updates from upload workerrNcopyz"Copying files to server (Robocopy)u📋r z3... Multi-threaded copy in progress Please wait...rlr\... ... File  of  Progress: %zipzCreating ZIP snapshot (local)u📦extractExtracting to server📂 Processing⚙️) hasattrrsetRangerHsetValuer setLabelTextr processEvents) r&r.r/r0r1 phase_texticon label_text percentagedisplay_filenames r(rz"VersionsWidget._on_upload_progressst.//  F F??=JDzz$--a333&&j&&& >CQYYS'E/S!8999A $--a555$--j999#+ '((2--',/?/E'E$//j//#//).//'//",/// e^^8JD9>go4555J  ) )!S 1 1 1  ) )* 5 5 5' #$$r))#(+;CDD+A#A ++*++++%*++#++(+++ Ji  /JD9>go4555J  ) )!S 1 1 1  ) )* 5 5 5' #$$r))#(+;CDD+A#A ++*++++%*++#++(+++ J&JD 22:222J ))*555"$$$$$r)cjt|dr)|jt|d|dr|dd}t |dz}t |dz}|dkr|d|dn|d}t j|dd |jd |d d |d dz dd|d|d |j |j | dSt j |d|ddS)zHandle upload completionrsuccess elapsed_timer<zm sSuccess Version 'z!' uploaded successfully! Files: rz Size: rrrz MB Time: z ZIP: zip_pathz Upload FailedmessageN)r/rclosedelattrgetrHr informationrrclearrrSr)r&r@elapsedelapsed_minuteselapsed_secondsrs r(rz"VersionsWidget._on_upload_finished+s 4* + + -  & & ( ( ( D+ , , , )  Kjj33G!'R-00O!'B,//OCRUVCVCV/??_????_n\q\q\qH  #-D5-- /-- -;F--"--z* --     " ( ( * * *  " ( ( * * * LLNNNNN  y8I J J J J Jr)ct|dr)|jt|dt jd|t j|dd|dS)zHandle upload failurerzUpload failed: rzFailed to upload version: N)r/rrBrCrr=r rr& error_msgs r(rz VersionsWidget._on_upload_failedKsx 4* + + -  & & ( ( ( D+ , , , 2y22333T7,T,T,TUUUUUr)c|}|sdS|j|}|sdStj|d|ddd}|sdSt |}|rt|ritj |dd|ddtj j tj j ztj j }|tj j krdStd dd d ||_|jd |jt$jj|jd |jd |jt1d|j|||_|jj|j|jj|j|jj |j!|d|_"||_#tIj%d||j&dS)z'Download selected version to local pathNz Choose destination for version 'rr rzDestination Not Emptyu1⚠ WARNING ⚠ The destination directory is not empty. Download behavior: • Files in ZIP will OVERWRITE existing files • Files NOT in ZIP will be KEPT • This may leave obsolete files from previous versions 💡 Recommendation: Download to an empty directory or create a subdirectory (e.g., 'vz'). Continue anyway?zInitializing download...rrlzDownloading VersionFr4) version_id destinationz%Starting download worker for version )'rrNget_project_versionrgetExistingDirectoryrexistsanyiterdirr rrrrrdownload_progressrrrrrrrrrr$download_workerr,rv_on_download_progressr;_on_download_finishedr>_on_download_failed_download_version_name_download_destinationrrr)r&rNrrO dest_pathr s r(rz VersionsWidget._download_versionTs2244   F'--j99  F"6  Iw~/F I I I     F%%      #i&7&7&9&9":": ''$9@8O$$$*.1K1NN*-E  2666"1 &  s  " "  --.CDDD 001B1NOOO 11!444 ++E222 ##%%% -   !#     -55d6PQQQ /778RSSS -55d6NOOO'.n&=#%0"  HJHHIII ""$$$$$r)c ft|dsdS|dkrt||z dz}nd}|j||}t |dkr d|ddz}d|d |d |d |jd |d }|j|tjdS)z,Handle progress updates from download workerrUNrrlr\r"r#u📥 Extracting files... File r%r&z Destination: z Progress: r() r/rHrUr1rr[r2rr3)r&r.r/r0r1r7r8r6s r(rWz$VersionsWidget._on_download_progressst011  F 199go455JJJ '' 333$  2 % %$'7'==  ' ' '!& ' ' ' '!6 ' '$  ' ' '  ++J777"$$$$$r)c $t|dr)|jt|d|dr2t j|dd|jd|dd|dd St j|d |d d S) zHandle download completionrUr:r>r?z#' downloaded successfully! Files: rz Location: rOzDownload FailedrAN)r/rUrBrCr rErZr)r&r@s r(rXz$VersionsWidget._on_download_finisheds 4, - - /  " ( ( * * * D- . . . )  M  #5D755 /55#M255       '8&:K L L L L Lr)ct|dr)|jt|dt jd|t j|dd|dS)zHandle download failurerUzDownload failed: rzFailed to download version: N)r/rUrBrCrr=r rrKs r(rYz"VersionsWidget._on_download_failedsx 4, - - /  " ( ( * * * D- . . . 444555T7,V9,V,VWWWWWr)c |}|sdS|j|}|sdS|j|d}|sdSt j|dd|dd|dd|d d tjjtjjztjj}|tjjkrdStd dd d ||_ |j d|j tjj|j d |j d|j t'd|j||_|jj|j|jj|j|jj|j|d|_t=jd||j dS)z"Restore selected version to serverNr zRestore Versionu,⚠ WARNING ⚠ This will restore version 'rz' to the server. Location: r/rzw/ Any existing files will be REPLACED. A pre-restore backup will be created automatically (if files exist). Continue?zInitializing restore...rrlzRestoring VersionFr5)rNz$Starting restore worker for version )!rrNrPrr rrrrrrestore_progressrrrrrrrrrr$restore_workerr,rv_on_restore_progressr;_on_restore_finishedr>_on_restore_failed_restore_version_namerrr)r&rNrrr s r(rzVersionsWidget._restore_versionsW2244   F'--j99  F',,W\-BCC  F#   *1.*A   !34  7>v     & *[-G-J J  & )    K.2 2 2 F!0 %  s  ! !  ,,-@AAA //0A0MNNN 00333 **5111 ""$$$,   !    ,44T5NOOO .66t7PQQQ ,44T5LMMM&-^%<"  G:GGHHH !!#####r)c t|dsdS|dkrt||z dz}nd}|j||dkrd}d}n|dkrd }d }nd }d }|}t |d kr d|ddz}|d|d|d|d|d|d } |j| t jdS)z+Handle progress updates from restore workerrbNrrlbackupzCreating pre-restore backupu💾r*r+r,r-r.r\r"r#r!r$r%r&r'r()r/rHrbr1rr2rr3) r&r.r/r0r1r7r4r5r8r6s r(rdz#VersionsWidget._on_restore_progresssMt/00  F 199go455JJJ &&z222 H  6JDD i  /JDD%JD$  2 % %$'7'==  ' 'j ' ' ' '!& ' ' ' '$ ' ' '  **:666"$$$$$r)ct|dr)|jt|d|drpd|jd}|d|ddz }|dr|d |dd z }n|d z }t j|d ||dSt j |d |ddS)zHandle restore completionrbr:r?z' restored successfully! zFiles: rr&backup_version_namezBackup created: 'r z No backup needed (first restore)r>zRestore FailedrAN) r/rbrBrCrgrDr rErSr)r&r@rAs r(rez#VersionsWidget._on_restore_finished<s 4+ , , .  ! ' ' ) ) ) D, - - - )  LZ$"<ZZZG : !6::: :Gzz/00 >Ov6K/LOOOO==  #D)W = = = LLNNNNN  '7 9J K K K K Kr)ct|dr)|jt|dt jd|t j|dd|dS)zHandle restore failurerbzRestore failed: rzFailed to restore version: N)r/rbrBrCrr=r rrKs r(rfz!VersionsWidget._on_restore_failedPsx 4+ , , .  ! ' ' ) ) ) D, - - - 3 33444T7,U),U,UVVVVVr)c|}|sdS|j|}|sdStj|dd|ddtjjtjjz}|tjjkr |j |}|dr-tj d|| dStj |d|d dS#t$r;}tjd |tj |d d |Yd}~dSd}~wwxYwdS) zDelete selected versionNzDelete VersionzDelete version 'rzT'? This will delete the ZIP file and database record. This action cannot be undone.r:zDeleted version z Delete FailedrAzError deleting version: rzFailed to delete version: )rrNrPr rrrrr$delete_versionrrrSrr<r=)r&rNrr r@rAs r(rzVersionsWidget._delete_versionYs2244   F'--j99  F$   -w~6 - - -  & *[-G-J J    K.2 2 2 V-<s.. jllO8_J4J4J4X""" %Q%Q%QN3Q3Q3Qj///$:::<<< ( ( ( ( ( (VVVBB#B#B#HN%N%N%`KKK@VVVJ%J%J%X%%%:MMM"XXX<$<$<$|%%%%%%NLLL(WWWVVVVVVVr)rMN)&rFrrrrrrrr r r r r rrrr PyQt6.QtCorerrrrG PyQt6.QtGuirrlogururrpathlibr core.databaser core.configrcore.version_managerrrrMr6r)r(rvs GFFFFFFFFFFF%%%%%%%%""""""''''''//////&/&/&/&/&/G&/&/&/Rz Vz Vz Vz Vz VWz Vz Vz Vz Vz Vr)