igdZddlZddlZddlmZmZddlmZmZmZm Z m Z m Z m Z m Z mZmZmZmZddlmZddlmZmZmZddlmZddlmZdd lmZdd lmZdd l m!Z!dd l"m#Z#dd l$m%Z%GddeZ&dS)zu Dashboard Widget for Python Scheduler Control panel style with critical jobs, active executions, and recent history N)datetime timedelta) QWidget QVBoxLayout QHBoxLayoutQLabelQFrame QScrollArea QGroupBox QGridLayout QTableWidgetQTableWidgetItem QPushButton QHeaderView)Qt)QFontQColorQPalette) CronTrigger)IntervalTrigger) DateTrigger)logger)SchedulerDatabase)SchedulerConfig)convert_unix_to_iso_dayofweekceZdZdZdedeffd ZdZdedede fd Z d Z d Z d Z d ZdZdZdZdZdZdZdZdZdedefdZdededefdZdedefdZdedefdZdZxZS)DashboardWidgetz"Dashboard with control panel styledbconfigc t||_||_|t j|_|| dS)N) super__init__rr_get_week_startrnowcurrent_week_start _setup_uirefresh)selfrr __class__s 'C:\PYTHON\Frank\gui\dashboard_widget.pyr"zDashboardWidget.__init__sd  "&"6"6x|~~"F"F  ct|}|dddd|dt}|dd|_t|_|j|j||jd|dd|_ t|_ |j |j ||j d| ||dd|_ t}|d |t jj|d t)}t|_||j||t}|||j |||j d|d d |_t|_|j|j||jd |dS)zSetup dashboard UIu🔴 CRITICAL JOBSz#ffebeeu▶️ ACTIVE NOWz#e8f5e9u📊 RECENT EXECUTIONSz#e3f2fdTi,u📅 WEEKLY TIMELINEz#fff3e0N)rsetContentsMargins setSpacingr _create_panelcritical_panelcritical_content setLayout addWidget active_panelactive_content addLayout recent_panelr setWidgetResizable setFrameShaper ShapeNoFramesetMaximumHeightrrecent_content setWidgettimeline_paneltimeline_content addStretch)r( main_layouttop_row recent_scrollrecent_container panel_layouts r*r&zDashboardWidget._setup_ui's|!$'' &&r2r2666r"""--#001EyQQ +  %%d&;<<<$-q111!../BINN)mm ##D$7888$+Q///g&&&!../GSS$ ((...##FL$8999&&s+++#99)mm""4#6777 0111#}} }--- ##L111d/333#001GSS +  %%d&;<<<d11555     r+titlebg_colorreturncVt|}|d|d|S)zCreate a styled panela  QGroupBox { font-size: 14px; font-weight: bold; border: 2px solid #ccc; border-radius: 8px; margin-top: 10px; padding: 15px; background-color: z; } QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 5px; } )r setStyleSheet)r(rJrKpanels r*r2zDashboardWidget._create_panel\sI%    $,      " r+c ||||dS#t$r"}t jd|Yd}~dSd}~wwxYw)zRefresh dashboard datazError refreshing dashboard: N)_refresh_critical_jobs_refresh_active_jobs_refresh_recent_executions_refresh_weekly_timeline Exceptionrerror)r(es r*r'zDashboardWidget.refreshts =  ' ' ) ) )  % % ' ' '  + + - - -  ) ) + + + + + = = = L;;; < < < < < < < < < =sAA BA;;Bc||j|jdt j}d|D}|s?t d}|d|j|nL|D]I}t d|d}|d|j|J|j d S) zRefresh critical jobs sectionT enabled_onlyhostnamec*g|]}|ddk|S) criticalitycritical).0js r* z:DashboardWidget._refresh_critical_jobs..s'OOOq-0@J0N0N0N0N0Nr+zNo critical jobs configured color: #666; font-style: italic;u● namez3font-size: 12px; color: #c62828; font-weight: bold;N) _clear_layoutr4r get_all_jobssocket gethostnamerrNr6rD)r(all_jobs critical_jobslabeljob job_labels r*rQz&DashboardWidget._refresh_critical_jobs~s  401117''TFDVDXDX'YYOOHOOO  ;899E    B C C C  ! + +E 2 2 2 2$ ; ;"#7#f+#7#788 ''(]^^^%// :::: ((*****r+cl||j|jt j}|s?t d}|d|j|n|D]}t d|d}|d|j|t d|d}|d |j||j d S) zRefresh active jobs section)r[zNo jobs runningrcu▶ job_namez3font-size: 12px; color: #2e7d32; font-weight: bold;z Status: statuszfont-size: 10px; color: #666;N) rer8rget_active_executionsrgrhrrNr6rD)r(activerk executionrm status_labels r*rRz$DashboardWidget._refresh_active_jobss@ 4.///..8J8L8L.MM <,--E    B C C C   ) )% 0 0 0 0# < < "#A)J*?#A#ABB ''(]^^^#--i888 &&IIh4G&I&IJJ **+JKKK#--l;;;; &&(((((r+c & ||j|jdd}d|j|t jD}|s@td}| d|j |nIg}|D]}t|}|r}|dd|dkre|dd xxd z cc<|dd  |d |dd  |dd |d <|d g|d <|dg|d <| ||D]~}|d }|d }td|D} t} | tjjtjjz| d | dt+| } | dddd|dr|dddnd} t| } | d| d| | |d kr| rd}n||d }t|}|d| ||d}t|}| d| |d |d krwtd|}| d|ddd|d Dz| ||d r |d d!d"}nd}t|}|d#|t8jj| d| ||d$t?|d$nd}td%|}|d#|t8jj| d| ||j | |j dS)&uKRefresh recent executions section — consecutive same-job runs are groupedzgui.dashboard_recent_jobsr-c>g|]}|dd|S)show_on_dashboard_valuer.get)r`exs r*rbz>DashboardWidget._refresh_recent_executions..s=   vv/33    r+)limitr[zNo recent executionsrcro_countr. _statusesrp_times start_timec3K|]}|dvV dS))failedtimeoutNr_)r`ss r* z=DashboardWidget._refresh_recent_executions..s(!O!O!'<".s/!K!KQ!K!CRC&!K!K!K!K!K!Kr+duration_secondsz.1fr< exit_codezExit: )!rer@rryrget_recent_executionsrgrhrrNr6dictappendanyr setFrameStyler=BoxShadowPlain setLineWidthrr0 setFixedWidth_get_status_icon setToolTipjoin setAlignmentr AlignmentFlag AlignRightstrrD)r(r{recentrkgroupedrsrowcountstatuses has_failure exec_frame exec_layouttime_str time_label status_iconrt name_text name_labelbadge duration_strduration_label exit_code_str exit_labels r*rSz*DashboardWidget._refresh_recent_executionss\ 4.///  ;R@@  66UVM_MaMa6bb    U :122E    B C C C   ) )% 0 0 0 0G# ( ( 9oo(wr{:6#j/IIBK)))Q.)))BK ,33CMBBBBK)00\1BCCCC$%CM(+H C $%(%6$7CMNN3''''$B :B : !(+$[1!!O!Oh!O!O!OOO #XX (()9FM} | | dzz} | dzdz}| dd|d}| | tC|?|"}|#| || $d| %t j&j'| (t j)j*| +d| ,t j-j.| /t j-j.| 0t j1j2| 3d| 4t*j5j6| 78d| 79dt?d D]9}| :;|txj=j>:|j| |)| ?|dSdS)z%Refresh weekly timeline calendar viewNr.u◀ Prevrdaysz%d %bz - z%d %b %Yz#font-weight: bold; font-size: 12px;TodayuNext ▶r)MonTueWedThuFrir/r02d:TFi^)@rCritemAtwidget isinstancer verticalScrollBarvaluererrrclickedconnect _prev_weekr6r%rrstrftimerrr AlignCenterrN_reset_to_current_week _next_weekr9setColumnCount setRowCountsetHorizontalHeaderLabelsrangesetVerticalHeaderItemr_get_week_executions_populate_timeline_tablesetAlternatingRowColorssetEditTriggers EditTriggerNoEditTriggerssetSelectionMode SelectionMode NoSelection setAutoScrollsetVerticalScrollMode ScrollModeScrollPerPixelsetHorizontalScrollModesetSizeAdjustPolicySizeAdjustPolicyAdjustToContentsOnFirstShowsetFixedHeightsetFocusPolicy FocusPolicyNoFocusverticalHeadersetDefaultSectionSize setVisiblehorizontalHeadersetSectionResizeModer ResizeModeStretchsetValue)r( scroll_pos old_table nav_layoutprev_btnweek_end week_label today_btnnext_btntableheaders start_hourrhourminuter executionscols r*rTz(DashboardWidget._refresh_weekly_timelines  & & ( (1 , ,-44Q77>>@@I)\22 C&88::@@BB  40111!]] z**r"""  111X&&&*YA->->->>t6??HHllXM^M^_iMjMjllmm  0 <===  !FGGGZ+++(( ###!!$"=>>>Y'''z**r"""  111X&&& '' 333 Q "655 ''000 99 K KC*DAg^F 333v333J  ' '-=j-I-I J J J J..00  %%eZ888 %%d+++ l6EFFF |9EFFF E""" ##L$;$JKKK %%l&=&LMMM !!,"?"[\\\ S!!! R^3444 44R888 ))$///88 _ _C  " " $ $ 9 9#{?U?] ^ ^ ^ ^ ''...  !  # # % % . .z : : : : : " !r+cz|t|z ddddS)z%Get Monday of the week for given daterr)rrsecond microsecond)rweekdayreplace)r(dates r*r#zDashboardWidget._get_week_startns9ydllnn5555>>AaXYgh>iiir+cj|xjtdzc_|dS)zNavigate to previous weekrNr%rrTr(s r*rzDashboardWidget._prev_weekr9 9!#4#4#44 %%'''''r+cj|xjtdz c_|dS)zNavigate to next weekr rNr r s r*rzDashboardWidget._next_weekwr r+c|tj|_|dS)zReset to current weekN)r#rr$r%rTr s r*rz&DashboardWidget._reset_to_current_week|s6"&"6"6x|~~"F"F %%'''''r+c  ddl}|d}||j}|t dz}|jdtj}g}|D]}|dd kr|d d kr| d 3| d dsJ t|dtrtj |dn|d}n7#t$r*} tjd|dd| Yd} ~ d} ~ wwxYw||d|} | sd} |} d} d}|| kr | | | }n7#t$r*} tjd|dd| Yd} ~ ndd} ~ wwxYw|r||krnS|||d|d|d |d d|} |t dz} |dz }|| k|S#t$r%} tjd| dgcYd} ~ Sd} ~ wwxYw)z2Calculate all schedule executions for current weekrN Europe/Romer rTrY schedule_typemanual target_type sync_profile target_namerwr.schedule_configz-Failed to parse schedule config for schedule id: iz(Error calculating next_run for schedule rd)r schedule_id schedule_namerr)secondszError in _get_week_executions: )exc_info)pytztimezonelocalizer%rrget_all_schedulesrgrhryrrjsonloadsrUrrV_build_trigger_for_timelineget_next_fire_timer)r(rtz week_startr schedulesrschedulerrWtriggerpreviousr$max_iterations iterationsnext_runs r*rz$DashboardWidget._get_week_executionssI  KKK}--BT%<==J!I1$5$5$55H11tfN`NbNb1ccIJ%8 $8 $O,88M*n<<<< ..6 ||$=qAAHRS[\mSnpsHtHtVTZ1B(CDDD{CDU{VFF L!fQYZ^Q_!f!fcd!f!fgggHHHH ::8O;TV\]]  !%  >11#*#=#=h#L#L$ %ePXY]P^%e%ebc%e%efff$x8';';%%$,'/~)1&)9'/ '>'/ '> '' (H"Yq%9%9%99C!OJ)!>11,     L>1>> N N N NIIIIII ssCH=DH D6 D1,H1D660H'E>=H> F2 F-(H-F22A"H IH?9I?Icz d}|dkr>|d}|sdSt|}tj||S|dkrt d d|i|S|dkr*|d}|sdSt || SdS#t $r"}tjd |Yd}~dSd}~wwxYw) z3Build APScheduler trigger for timeline calculationsrcronNrintervalrrrun_dater2rzCould not build trigger: r_) ryrr from_crontabrrrUrwarning)r(rrr% cron_expr cron_expr_isor2rWs r*r#z+DashboardWidget._build_trigger_for_timelines B&&"JJv..   4 =i H H "/ KKKK*,,&===f===&((!::j11 4"HrBBBBt    N:q:: ; ; ;44444 s.B$BBB;B B:B55B:c i}|D]}|d}|jr(|d}|}|j}|j}|dks|dks|dkr|dks|dkrz|dz dz|d krd ndz} | |f} | |vri|| <|d } | || vrg|d |d d|| | <|| | d||D]\\} } }t| }t|}|d krD|d} t|| d}|d kr| ddd|}n| dd}n|d}g}|D]\} }t|d}|d }|d kr@|dd d}||d| d|dhd d|dddD}|dkr|dz }|d|d| d|d|d t|}|d ||t jjd |D}|d!}|d"}|d#}|dkr/|dkr)|dkr#|t-d$n|dkr/|dkr)|dkr#|t-d%nW|dkr/|dkr)|dkr#|t-d&n"|t-d'|| | |t1d(D]N} t1dD]<} || | $|| | td)=OdS)*uQPopulate timeline table — deduplicated by schedule name within each 30-min slotrN)tzinforrrr/rr.rrr) fire_timesrrr; u ×z tasks%H:%Mz z (), c3@K|]}|dVdS)r>N)r)r`fts r*rz;DashboardWidget._populate_timeline_table..s.&^&^r{{7';';&^&^&^&^&^&^r+u…rz [z] (rcg|] }|d S)rr_)r`sdatas r*rbz.)sMMMeU=)MMMr+backup_profilerlgroupz#bbdefbz#c8e6c9z#e1bee7z#fff9c4r)r9 astimezonerrrrritemslistkeyslenrrrrsetTextAlignmentrrrvaluesr setBackgroundrsetItemritem)r(rrslotsrsexec_dt day_of_weekrr slot_indexkeysnamerr schedule_map unique_namesn_uniquefires cell_text tooltip_linesrEtargetrsamplerRtypes backup_count job_count group_counts r*rz(DashboardWidget._populate_timeline_tables# < >199#(": 9 9% 9 9II %crc II'/// M , 2 2 4 4 X X uE,/00}-A::l+A.77@@A!((A)D)D)D)D6)D)D)DEEEE!YY&^&^eLFYZ\[\Z\F]&^&^&^^^Fqyy%!(()Ve)V)Vu)V)V)V)VV)V)V)VWWWW#I..D OODIIm44 5 5 5  ! !""2"> ? ? ?NM|7J7J7L7LMMME ;;'788L E**I++g..KaINN{a7G7G""6)#4#45555Q<1#4#49I9I""6)#4#45555q\Q%6%69>>""6)#4#45555""6)#4#4555 MM#sD ) ) ) )99 B BCQxx B B::c3''/MM#s,>@@ @ J & &EG 3 3'++C003LLE!1!1C!1!122227P.DIIe,,...=P P F " "H 3 3J F FHH H H $ $%%=!!!r+r(c|dpd|d}dddd}||d|dd }|d |S) zLabel target with its typerzID target_idJobGroupBackup)rlrGrFrTargetrrx)r(r(rtype_mapprefixs r*_format_targetzDashboardWidget._format_targetmsll=11V5V8<< ;T;T5V5V &   hll=998<< W_;`;`aa))K)))r+rpc@ddddddd}||dS) zGet status icon for executionu✅u❌u▶️u⏱️u⛔u⏳) completedrrunningr cancelledqueuedu❓rx)r(rpiconss r*rz DashboardWidget._get_status_iconxs8   yy'''r+cp|r|d}|r'|n;|r'|||dSdS)zClear all widgets from layoutrN)rtakeAtr deleteLaterlayoutre)r(rrRs r*rezDashboardWidget._clear_layoutsllnn 2==##D{{}} 2 ))++++ 2""4;;==111 llnn 2 2 2 2 2r+)__name__ __module__ __qualname____doc__rrr"r&rr r2r'rQrRrSrTr#rrrrr#rrrirpryrre __classcell__)r)s@r*rrs,,,o3!3!3!j3#)0===+++*)))2a)a)a)F];];];~jjj((( ((( ((( KKKZ0WBWBWBrt:"3""RU""""" *t * * * * * (s (s ( ( ( (2222222r+r)'rr!rgrrPyQt6.QtWidgetsrrrrr r r r r rrr PyQt6.QtCorer PyQt6.QtGuirrrapscheduler.triggers.cronrapscheduler.triggers.intervalrapscheduler.triggers.daterlogurur core.databaser core.configrcore.cron_utilsrrr_r+r*rs  (((((((( //////////111111999999111111++++++''''''999999p 2p 2p 2p 2p 2gp 2p 2p 2p 2p 2r+