9iCSrSSKrSSKrSSKrSSKrSSKrSSKJr SSKJr SSK J r J r J r SSK Jr SSKrSSKJr SSKJr "S S 5rg) z Job Executor for Python Scheduler Handles job execution in background threads with timeout support Based on DataHub web_panel/utils/job_manager.py pattern N)datetime)Path)OptionalDictAny)logger)SchedulerDatabase)SchedulerConfigc\rSrSrSrSS\S\4SjjrS\4Sjr SS \S \ S \ \4S jjr S \S\ \ \44SjrS\ \ \4S \4SjrS\ \ S \ 4SjrS \S\ S \4SjrSS\S\S \4SjjrS \S\ \ \44SjrS \4SjrS \S \4SjrSrg) JobExecutorz8Executes jobs in background threads with proper trackingNdbconfigcXlX lX0l0Ul[R "5Ul[R"S5 g)NzJobExecutor initialized) rrnotification_manager active_jobs threadingLocklockrinfo)selfrrrs *C:\PYTHON\Scheduler\daemon\job_executor.py__init__JobExecutor.__init__s5 $8!NN$  -.pidc[R"U5nURSS9nUHHn[R"SUR SUR 535 UR5 MJ [R"SUR SUR 535 UR5 [R"U/U-SS9upVUHHn[R"SUR SUR 535 UR5 MJ g ![Ra GMf=f![Ra Nf=f![Ra Mf=f![Ra [R"S US 35 g [a&n[R"S USU35 S nAg S nAff=f) z\ Terminate process and all its children recursively Args: pid: Process ID to terminate T) recursivezKilling child process : zKilling parent process timeoutzForce killing process zProcess z already terminatedzError killing process tree N) psutilProcesschildrenrdebugrnamekill NoSuchProcess wait_procswarning Exceptionerror) rrparentr%childgonealivepes r_kill_process_treeJobExecutor._kill_process_tree!s! C^^C(F6H"LL#9%))Buzz|n!UVJJL"  6vzzl"V[[]OTU !++VHx,?KKDNN%;AEE7"QVVXJ#OPFFH++''  ++## @ NNXcU*=> ? C LL6se2aSA B B Cs*E5AD$1E56AE: E5AEE5$D=8E5<D==E5EE5EE5E2.E51E22E55.G% G.GGjob_id triggered_byreturnc URRU5nU(d[R"SUS35 gUS(d![R"SUSSUS35 gURR "SUS U[ R"5S .UD6n[R"S USS US 35 [R"URXT4SS9nUR5 U$)z Execute a job asynchronously Args: job_id: ID of job to execute triggered_by: Who/what triggered execution **kwargs: Additional execution parameters Returns: Execution ID for tracking, or None if job not found/disabled zJob z not foundNenabledJob 'r'z' (ID z) is disabled, skippingqueued)r6statusr7 start_timez Queued job 'z' (execution ID )F)targetargsdaemon) rget_jobrr-r+create_executionrnowrrThread_run_jobstart)rr6r7kwargsjob execution_idthreads r execute_jobJobExecutor.execute_jobKsggoof% LL4xz2 39~ NNU3v;-vfX=TU Vww// %||~      l3v;-/? ~QOP!!==$   rrLrKc RUSnURX5nURRUS[R"5[ U5S9 [ R"SUSUS35 URU5n[R"5nURSS5nU(Ga[ R"S US 35 [US S S 9nURS5 URSUS35 URSUS35 URSURS5S35 URS[U[ 5(aUOSRU5S35 URSUSS35 URSS35 URS5 URS5 SSS5 [ R"S US35 [U[ 5(aSSKn U R!U5n[USS S 9n["R$"UUS(aUSOSU["R&S S!9n SSS5 UR( W UR*U'SSS5 US"n W R-U S#9n U S:XaS$OS%n UR( UR*R7US5 SSS5 GO{[US S S 9nURS5 URSUS35 URSUS35 URSURS5S35 URSSRU5S35 URSUSS35 URSS35 UR95 ["R$"UU["R&US(aUSOSS S,9n UR( XR*U'SSS5 US"n U R-U S#9n U S:XaS$OS%n UR( UR*R7US5 SSS5 SSS5 [R"5nX- R;5nUR=U5unnURRUW UUW UUS-9 U S$:Xa[ R"S US.US/S035 O@U S+:Xa[ R>"S US&W S035 O[ R>"S US1U 35 UR@(aUR@RCUUU UU S29 U S%:XaUS3S:aUREX5 ggg!,(df  GN=f!,(df  GN"=f!,(df  GN=f!["R.ag [ R0"S US&U S'35 [ R"S(US)W R2S35 UR5U R25 S*n S+n GNnf=f!,(df  GN=f!,(df  GN==f!["R.ag [ R0"S US&U S'35 [ R"S(US)U R2S35 UR5U R25 S*n S+n GNf=f!,(df  GN}=f!,(df  GN=f![Fan[ RH"S4US5U35 URRUS%[R"5[ U5S69 [USS S 9nURSSS35 URS7US35 URSS35 SSS5 SnAg!,(df  SnAg=f! SnAg=fSnAff=f)8zo Internal: Execute job in subprocess Args: execution_id: Execution record ID job: Job definition dict r'running)r=r>log_filezStarting job 'z ' (execution r? show_consoleFr;zW' configured to show console window (debug mode) - output will be visible in CMD windowwutf-8encodingz=== Job Execution Log === zJob:  zExecution ID: z Start Time: z%Y-%m-%d %H:%M:%Sz Command:  zWorking Directory: working_directoryz2==================================================z z6NOTE: Output displayed in console window (debug mode) z6Log file not captured - see console window for output Nz:' has show_console enabled - log can be monitored from GUIraT)cwdstdoutstderrtexttimeout_secondsr! completedfailedz' timed out after z secondsz"Terminating process tree for job 'z' (PID: r")r]r^r\r_)r=end_timeduration_seconds exit_codestdout_previewstderr_previewz' completed successfully in z.1fsz' failed with exit code )rLjob_namer=durationrf retry_countzError executing job 'z': )r=rd error_messagezEXECUTION ERROR: )%_create_log_file_pathrupdate_executionrrFstrrr_build_commandgetopenwritestrftime isinstancejoinshlexsplit subprocessPopenSTDOUTrrwaitTimeoutExpiredr+rr4popflush total_seconds_read_log_previewsr-rnotify_job_result _handle_retryr, exception)rrLrKrj log_file_pathcmdr>rSrRrxprocessr"rfr=rdrkrgrhr3s rrHJobExecutor._run_jobus+v;22<J m  GG $ $ #<<>]+ %  KK. - ~QO P%%c*C"J77>59L eH:.EFG-w?8NN%@BNNU8*B#78NN^L>#DENN\*2E2EFY2Z1[[]#^_NNYjc6J6JsPSPXPXY\P].^^`#abNN%8=P9Q8RRT#UVNNhZt#45NN%\^NN%\^@ eH:-ghic3'' ++c*C-w?8(..8;#DENN\*2E2EFY2Z1[[]#^_NNYsxx}oR#@ANN%8=P9Q8RRT#UVNNhZt#45NN$)..')008;H.2-D-D]-S *NN GG $ $!!)#-- % $ eH:-I(SVWXYZ9$ uXJ.@ KL uXJ.FykRS(());;!-%!%' <!c-&81&<""<5'=!@?*@?Y"00'NNU8*4Fwix#XYKK"DXJhW^WbWbVccd ef++GKK8 "I&F 'Y2#&44+xj8J7)S['\] &H RZ[b[f[fZggh$ij// <$& !* +#K@?^    4XJc!E F GG $ $!!!f %  -w?8NNT(2#67NN%6qc#<=NNhZr?3@??? % sEB:[ C)U;A[ 6U#[ U5, [ :V [ X<[ DZ9X! Z9/X) Z9Z'1Z99D[  U [ # U2-[ 5 V?[ A7X>[ X[  X[  X& !Z9)A7Z$ Z9#Z$$Z9' Z6 1Z99 [[ ^&A^!* ^5?^4^ ^ ^^^^!^!!^&cUSnUSnUS:XaU/nOdUS:Xa$URURS55nXS/nO:US:Xa&URURS55nUSU/nO[SU35eUS (a-[R"US 5nUR U5 U$U$![R a URUS 5 U$f=f) zVBuild command list for subprocess Returns: list: Command and arguments as a list job_typeexecutable_pathbatchscriptrZ python_modulez-mzUnknown job type: arguments)_get_python_exerr ValueErrorjsonloadsextendJSONDecodeErrorappend)rrKrrr python_exerAs rrqJobExecutor._build_command/s z?/0 w "#C  !--cgg6I.JKJ/C  (--cgg6I.JKJt_5C1(<= = {  -zz#k"23 4  s '' - 3{+,  -s*B22(CCrZcSSKnU(a4[U5S- S- S- nUR5(a [U5$URR S05nU(aUR 5HzupVURUR SS55(dM-UR S5nU(dMG[U5S- S- nUR5(dMo[U5s $ UR$) z3Get Python executable, preferring venv if availablerNvenvScriptsz python.exeprojectsroot) sysrexistsrprrritems startswith executable)rrZr venv_pythonr project_nameproject_config venv_paths rrJobExecutor._get_python_exeTs 01F:YFUK!!##;'';;??:r2 080@, $//0B0B620NOO . 2 26 :I y&*9o &AL&P &--//#&{#33 1A~~rrjc [URR5nURSSS9 [R "5R S5nUR5RSS5RSS5nUSUS3nX6- $)z#Create log file path with timestampT)parentsexist_okz %Y%m%d_%H%M%SrY_/z.log) rrexecution_log_dirmkdirrrFrulowerreplace)rrLrjlog_dir timestampsanitized_name log_filenames rrn!JobExecutor._create_log_file_pathls~t{{445 dT 2LLN++O< !)11#s;CCCM()9+T: %%rr preview_linesc[USSS9nUR5nSSS5 W(aSRXB*S5OSnUS4$!,(df  N/=f![a#n[R "SU35 SnAgSnAff=f)z+Read last N lines from log file for previewrrUrVNrzCould not read log preview: )NN)rs readlinesrwr,rr+)rrrflinespreviewr3s rrJobExecutor._read_log_previewsws} mS7;q <:?bggeNO45BGD= <;  NN9!= > s- AA(A AA B"BBc :^^^TRRSTSS9n[SU55nUTS::a`TSm[R"STSS TS US -S TSS -S 3 5 UUU4Sjn[ R "USS9R5 gg)zHandle job retry logic id)limitr6c3:# UHoSS:XdM Sv M g7f)r=rbNrC).0exs r ,JobExecutor._handle_retry..sU'8xLH[R"T5 TRTSSS9 g)Nrretry)r7)timesleeprN)rK retry_delayrsr retry_job,JobExecutor._handle_retry..retry_jobs& ;'  T ArT)r@rBN)rget_recent_executionssumrrrrGrI)rrLrKrecent_executions failed_countrrs` ` @rrJobExecutor._handle_retrys!GG993t99UU'8UU 3}- -34K KK.V U;-GYZfijZjYkklmpq~mCDnDmEEFG H B   Id ; A A C .rcUR [URR55sSSS5 $!,(df  g=f)z(Get list of currently running executionsN)rlistrkeys)rs rget_active_executions!JobExecutor.get_active_executionss* YY((--/0YYs #: AcUR XR;a SSS5 gURUnUR5cUR5 UR SS9 URRUS[R"5S9 URRUS5 [R"SU35 SSS5 gSSS5 g![ R a UR5 Nf=f![a.n[R "S US U35 SnASSS5 gSnAff=f!,(df  g=f) zs Stop a running execution Args: execution_id: Execution ID to stop Returns: True if stopped successfully NFr r! cancelled)r=rdzStopped execution TzError stopping execution r)rrpoll terminater}rzr~r(rrorrFrrrr,r-)rrLrr3s rstop_executionJobExecutor.stop_executions5YY#3#33Y&&|4G||~%!%%'' Q / GG,,$*!)-$$((t<KK"4\N CD;Y & F+&44' '"!!LL#<\N"QC!PQ CY>!?YFsYE ED C(A#D $DD DD  ED</E<EE E)rrrrr)N) scheduler))__name__ __module__ __qualname____firstlineno____doc__r r rintr4rprrNrrrHrrqrrrntuplerrrboolr__static_attributes__rCrrr r sB/,/o/(Cc(CT(#(S(U]^aUb(TxSxtCH~xt#$sCx.#T#J#30 &# & & &SRW D#DDcND"1t1 -3-4-rr )rrzrrrrrpathlibrtypingrrrlogururr# core.databaser core.configr r rCrrrs?  && +'ttr