@@ -330,7 +330,37 @@ def log_test_results(self, result: ModelTextTestResult, target_dialect: str) ->
330330 """
331331
332332
333+ class SignalConsole (abc .ABC ):
334+ @abc .abstractmethod
335+ def start_signal_progress (
336+ self ,
337+ snapshot : Snapshot ,
338+ total_signals : int ,
339+ default_catalog : t .Optional [str ],
340+ environment_naming_info : EnvironmentNamingInfo ,
341+ ) -> None :
342+ """Indicates that signal checking has begun for a snapshot."""
343+
344+ @abc .abstractmethod
345+ def update_signal_progress (
346+ self ,
347+ snapshot : Snapshot ,
348+ signal_name : str ,
349+ signal_idx : int ,
350+ total_signals : int ,
351+ ready_intervals : Intervals ,
352+ check_intervals : Intervals ,
353+ duration : float ,
354+ ) -> None :
355+ """Updates the signal checking progress."""
356+
357+ @abc .abstractmethod
358+ def stop_signal_progress (self , snapshot : Snapshot ) -> None :
359+ """Indicates that signal checking has completed for a snapshot."""
360+
361+
333362class Console (
363+ SignalConsole ,
334364 PlanBuilderConsole ,
335365 LinterConsole ,
336366 StateExporterConsole ,
@@ -536,6 +566,30 @@ def update_snapshot_evaluation_progress(
536566 def stop_evaluation_progress (self , success : bool = True ) -> None :
537567 pass
538568
569+ def start_signal_progress (
570+ self ,
571+ snapshot : Snapshot ,
572+ total_signals : int ,
573+ default_catalog : t .Optional [str ],
574+ environment_naming_info : EnvironmentNamingInfo ,
575+ ) -> None :
576+ pass
577+
578+ def update_signal_progress (
579+ self ,
580+ snapshot : Snapshot ,
581+ signal_name : str ,
582+ signal_idx : int ,
583+ total_signals : int ,
584+ ready_intervals : Intervals ,
585+ check_intervals : Intervals ,
586+ duration : float ,
587+ ) -> None :
588+ pass
589+
590+ def stop_signal_progress (self , snapshot : Snapshot ) -> None :
591+ pass
592+
539593 def start_creation_progress (
540594 self ,
541595 snapshots : t .List [Snapshot ],
@@ -860,6 +914,8 @@ def __init__(
860914 self .table_diff_model_tasks : t .Dict [str , TaskID ] = {}
861915 self .table_diff_progress_live : t .Optional [Live ] = None
862916
917+ self .signal_status_tree : t .Optional [Tree ] = None
918+
863919 self .verbosity = verbosity
864920 self .dialect = dialect
865921 self .ignore_warnings = ignore_warnings
@@ -901,6 +957,9 @@ def start_evaluation_progress(
901957 audit_only : bool = False ,
902958 ) -> None :
903959 """Indicates that a new snapshot evaluation/auditing progress has begun."""
960+ # Add a newline to separate signal checking from evaluation
961+ self ._print ("" )
962+
904963 if not self .evaluation_progress_live :
905964 self .evaluation_total_progress = make_progress_bar (
906965 "Executing model batches" if not audit_only else "Auditing models" , self .console
@@ -1050,6 +1109,75 @@ def stop_evaluation_progress(self, success: bool = True) -> None:
10501109 self .environment_naming_info = EnvironmentNamingInfo ()
10511110 self .default_catalog = None
10521111
1112+ def start_signal_progress (
1113+ self ,
1114+ snapshot : Snapshot ,
1115+ total_signals : int ,
1116+ default_catalog : t .Optional [str ],
1117+ environment_naming_info : EnvironmentNamingInfo ,
1118+ ) -> None :
1119+ """Indicates that signal checking has begun for a snapshot."""
1120+ display_name = snapshot .display_name (
1121+ environment_naming_info ,
1122+ default_catalog if self .verbosity < Verbosity .VERY_VERBOSE else None ,
1123+ dialect = self .dialect ,
1124+ )
1125+ self .signal_status_tree = Tree (f"Checking signals for { display_name } " )
1126+
1127+ def update_signal_progress (
1128+ self ,
1129+ snapshot : Snapshot ,
1130+ signal_name : str ,
1131+ signal_idx : int ,
1132+ total_signals : int ,
1133+ ready_intervals : Intervals ,
1134+ check_intervals : Intervals ,
1135+ duration : float ,
1136+ ) -> None :
1137+ """Updates the signal checking progress."""
1138+ # Format checked intervals
1139+ check_display = []
1140+ for interval in check_intervals [:3 ]: # Show first 3 intervals
1141+ interval_str = _format_signal_interval (snapshot , interval )
1142+ if interval_str :
1143+ check_display .append (interval_str )
1144+ if len (check_intervals ) > 3 :
1145+ check_display .append (f"... and { len (check_intervals ) - 3 } more" )
1146+
1147+ # Format ready intervals
1148+ ready_display = []
1149+ for interval in ready_intervals [:3 ]: # Show first 3 intervals
1150+ interval_str = _format_signal_interval (snapshot , interval )
1151+ if interval_str :
1152+ ready_display .append (interval_str )
1153+ if len (ready_intervals ) > 3 :
1154+ ready_display .append (f"... and { len (ready_intervals ) - 3 } more" )
1155+
1156+ # Display signal name
1157+ tree = Tree (f"[{ signal_idx + 1 } /{ total_signals } ] { signal_name } { duration :.2f} s" )
1158+
1159+ # TODO: what about full models and other cases where these lists can be empty?
1160+ check_str = ", " .join (check_display ) if check_display else "no intervals"
1161+ tree .add (f"check: { check_str } " )
1162+
1163+ ready_str = ", " .join (ready_display ) if ready_display else "no intervals"
1164+ if ready_intervals == check_intervals :
1165+ ready_str = f"[green]ready: { ready_str } [/green]"
1166+ elif ready_intervals :
1167+ ready_str = f"[yellow]ready: { ready_str } [/yellow]"
1168+ else :
1169+ ready_str = f"[red]ready: { ready_str } [/red]"
1170+
1171+ tree .add (ready_str )
1172+ if self .signal_status_tree is not None :
1173+ self .signal_status_tree .add (tree )
1174+
1175+ def stop_signal_progress (self , snapshot : Snapshot ) -> None :
1176+ """Indicates that signal checking has completed for a snapshot."""
1177+ if self .signal_status_tree is not None :
1178+ self ._print (self .signal_status_tree )
1179+ self .signal_status_tree = None
1180+
10531181 def start_creation_progress (
10541182 self ,
10551183 snapshots : t .List [Snapshot ],
@@ -3810,22 +3938,35 @@ def _format_audits_errors(error: NodeAuditsErrors) -> str:
38103938 return " " + "\n " .join (error_messages )
38113939
38123940
3813- def _format_evaluation_model_interval (snapshot : Snapshot , interval : Interval ) -> str :
3941+ def _format_interval (snapshot : Snapshot , interval : Interval , prefix : str = "" ) -> str :
3942+ """Format an interval with an optional prefix."""
38143943 if snapshot .is_model and (
38153944 snapshot .model .kind .is_incremental
38163945 or snapshot .model .kind .is_managed
38173946 or snapshot .model .kind .is_custom
38183947 ):
38193948 inclusive_interval = make_inclusive (interval [0 ], interval [1 ])
38203949 if snapshot .model .interval_unit .is_date_granularity :
3821- return f"insert { to_ds (inclusive_interval [0 ])} - { to_ds (inclusive_interval [1 ])} "
3822- # omit end date if interval start/end on same day
3823- if inclusive_interval [0 ].date () == inclusive_interval [1 ].date ():
3824- return f"insert { to_ds (inclusive_interval [0 ])} { inclusive_interval [0 ].strftime ('%H:%M:%S' )} -{ inclusive_interval [1 ].strftime ('%H:%M:%S' )} "
3825- return f"insert { inclusive_interval [0 ].strftime ('%Y-%m-%d %H:%M:%S' )} - { inclusive_interval [1 ].strftime ('%Y-%m-%d %H:%M:%S' )} "
3950+ base = f"{ to_ds (inclusive_interval [0 ])} - { to_ds (inclusive_interval [1 ])} "
3951+ elif inclusive_interval [0 ].date () == inclusive_interval [1 ].date ():
3952+ # omit end date if interval start/end on same day
3953+ base = f"{ to_ds (inclusive_interval [0 ])} { inclusive_interval [0 ].strftime ('%H:%M:%S' )} -{ inclusive_interval [1 ].strftime ('%H:%M:%S' )} "
3954+ else :
3955+ base = f"{ inclusive_interval [0 ].strftime ('%Y-%m-%d %H:%M:%S' )} - { inclusive_interval [1 ].strftime ('%Y-%m-%d %H:%M:%S' )} "
3956+ return f"{ prefix } { base } " if prefix else base
38263957 return ""
38273958
38283959
3960+ def _format_signal_interval (snapshot : Snapshot , interval : Interval ) -> str :
3961+ """Format an interval for signal output (without 'insert' prefix)."""
3962+ return _format_interval (snapshot , interval )
3963+
3964+
3965+ def _format_evaluation_model_interval (snapshot : Snapshot , interval : Interval ) -> str :
3966+ """Format an interval for evaluation output (with 'insert' prefix)."""
3967+ return _format_interval (snapshot , interval , prefix = "insert" )
3968+
3969+
38293970def _create_evaluation_model_annotation (snapshot : Snapshot , interval_info : t .Optional [str ]) -> str :
38303971 if snapshot .is_audit :
38313972 return "run standalone audit"
0 commit comments