@@ -330,7 +330,36 @@ def log_test_results(self, result: ModelTextTestResult, target_dialect: str) ->
330
330
"""
331
331
332
332
333
+ class SignalConsole (abc .ABC ):
334
+ @abc .abstractmethod
335
+ def start_signal_progress (
336
+ self ,
337
+ snapshot : Snapshot ,
338
+ default_catalog : t .Optional [str ],
339
+ environment_naming_info : EnvironmentNamingInfo ,
340
+ ) -> None :
341
+ """Indicates that signal checking has begun for a snapshot."""
342
+
343
+ @abc .abstractmethod
344
+ def update_signal_progress (
345
+ self ,
346
+ snapshot : Snapshot ,
347
+ signal_name : str ,
348
+ signal_idx : int ,
349
+ total_signals : int ,
350
+ ready_intervals : Intervals ,
351
+ check_intervals : Intervals ,
352
+ duration : float ,
353
+ ) -> None :
354
+ """Updates the signal checking progress."""
355
+
356
+ @abc .abstractmethod
357
+ def stop_signal_progress (self ) -> None :
358
+ """Indicates that signal checking has completed for a snapshot."""
359
+
360
+
333
361
class Console (
362
+ SignalConsole ,
334
363
PlanBuilderConsole ,
335
364
LinterConsole ,
336
365
StateExporterConsole ,
@@ -536,6 +565,29 @@ def update_snapshot_evaluation_progress(
536
565
def stop_evaluation_progress (self , success : bool = True ) -> None :
537
566
pass
538
567
568
+ def start_signal_progress (
569
+ self ,
570
+ snapshot : Snapshot ,
571
+ default_catalog : t .Optional [str ],
572
+ environment_naming_info : EnvironmentNamingInfo ,
573
+ ) -> None :
574
+ pass
575
+
576
+ def update_signal_progress (
577
+ self ,
578
+ snapshot : Snapshot ,
579
+ signal_name : str ,
580
+ signal_idx : int ,
581
+ total_signals : int ,
582
+ ready_intervals : Intervals ,
583
+ check_intervals : Intervals ,
584
+ duration : float ,
585
+ ) -> None :
586
+ pass
587
+
588
+ def stop_signal_progress (self ) -> None :
589
+ pass
590
+
539
591
def start_creation_progress (
540
592
self ,
541
593
snapshots : t .List [Snapshot ],
@@ -860,6 +912,8 @@ def __init__(
860
912
self .table_diff_model_tasks : t .Dict [str , TaskID ] = {}
861
913
self .table_diff_progress_live : t .Optional [Live ] = None
862
914
915
+ self .signal_status_tree : t .Optional [Tree ] = None
916
+
863
917
self .verbosity = verbosity
864
918
self .dialect = dialect
865
919
self .ignore_warnings = ignore_warnings
@@ -901,6 +955,9 @@ def start_evaluation_progress(
901
955
audit_only : bool = False ,
902
956
) -> None :
903
957
"""Indicates that a new snapshot evaluation/auditing progress has begun."""
958
+ # Add a newline to separate signal checking from evaluation
959
+ self ._print ("" )
960
+
904
961
if not self .evaluation_progress_live :
905
962
self .evaluation_total_progress = make_progress_bar (
906
963
"Executing model batches" if not audit_only else "Auditing models" , self .console
@@ -1050,6 +1107,88 @@ def stop_evaluation_progress(self, success: bool = True) -> None:
1050
1107
self .environment_naming_info = EnvironmentNamingInfo ()
1051
1108
self .default_catalog = None
1052
1109
1110
+ def start_signal_progress (
1111
+ self ,
1112
+ snapshot : Snapshot ,
1113
+ default_catalog : t .Optional [str ],
1114
+ environment_naming_info : EnvironmentNamingInfo ,
1115
+ ) -> None :
1116
+ """Indicates that signal checking has begun for a snapshot."""
1117
+ display_name = snapshot .display_name (
1118
+ environment_naming_info ,
1119
+ default_catalog if self .verbosity < Verbosity .VERY_VERBOSE else None ,
1120
+ dialect = self .dialect ,
1121
+ )
1122
+ self .signal_status_tree = Tree (f"Checking signals for { display_name } " )
1123
+
1124
+ def update_signal_progress (
1125
+ self ,
1126
+ snapshot : Snapshot ,
1127
+ signal_name : str ,
1128
+ signal_idx : int ,
1129
+ total_signals : int ,
1130
+ ready_intervals : Intervals ,
1131
+ check_intervals : Intervals ,
1132
+ duration : float ,
1133
+ ) -> None :
1134
+ """Updates the signal checking progress."""
1135
+ tree = Tree (f"[{ signal_idx + 1 } /{ total_signals } ] { signal_name } { duration :.2f} s" )
1136
+
1137
+ formatted_check_intervals = [_format_signal_interval (snapshot , i ) for i in check_intervals ]
1138
+ formatted_ready_intervals = [_format_signal_interval (snapshot , i ) for i in ready_intervals ]
1139
+
1140
+ if not formatted_check_intervals :
1141
+ formatted_check_intervals = ["no intervals" ]
1142
+ if not formatted_ready_intervals :
1143
+ formatted_ready_intervals = ["no intervals" ]
1144
+
1145
+ # Color coding to help detect partial interval ranges quickly
1146
+ if ready_intervals == check_intervals :
1147
+ msg = "All ready"
1148
+ color = "green"
1149
+ elif ready_intervals :
1150
+ msg = "Some ready"
1151
+ color = "yellow"
1152
+ else :
1153
+ msg = "None ready"
1154
+ color = "red"
1155
+
1156
+ if self .verbosity < Verbosity .VERY_VERBOSE :
1157
+ num_check_intervals = len (formatted_check_intervals )
1158
+ if num_check_intervals > 3 :
1159
+ formatted_check_intervals = formatted_check_intervals [:3 ]
1160
+ formatted_check_intervals .append (f"... and { num_check_intervals - 3 } more" )
1161
+
1162
+ num_ready_intervals = len (formatted_ready_intervals )
1163
+ if num_ready_intervals > 3 :
1164
+ formatted_ready_intervals = formatted_ready_intervals [:3 ]
1165
+ formatted_ready_intervals .append (f"... and { num_ready_intervals - 3 } more" )
1166
+
1167
+ check = ", " .join (formatted_check_intervals )
1168
+ tree .add (f"Check: { check } " )
1169
+
1170
+ ready = ", " .join (formatted_ready_intervals )
1171
+ tree .add (f"[{ color } ]{ msg } : { ready } [/{ color } ]" )
1172
+ else :
1173
+ check_tree = Tree ("Check" )
1174
+ tree .add (check_tree )
1175
+ for interval in formatted_check_intervals :
1176
+ check_tree .add (interval )
1177
+
1178
+ ready_tree = Tree (f"[{ color } ]{ msg } [/{ color } ]" )
1179
+ tree .add (ready_tree )
1180
+ for interval in formatted_ready_intervals :
1181
+ ready_tree .add (f"[{ color } ]{ interval } [/{ color } ]" )
1182
+
1183
+ if self .signal_status_tree is not None :
1184
+ self .signal_status_tree .add (tree )
1185
+
1186
+ def stop_signal_progress (self ) -> None :
1187
+ """Indicates that signal checking has completed for a snapshot."""
1188
+ if self .signal_status_tree is not None :
1189
+ self ._print (self .signal_status_tree )
1190
+ self .signal_status_tree = None
1191
+
1053
1192
def start_creation_progress (
1054
1193
self ,
1055
1194
snapshots : t .List [Snapshot ],
@@ -3810,19 +3949,34 @@ def _format_audits_errors(error: NodeAuditsErrors) -> str:
3810
3949
return " " + "\n " .join (error_messages )
3811
3950
3812
3951
3952
+ def _format_interval (snapshot : Snapshot , interval : Interval ) -> str :
3953
+ """Format an interval with an optional prefix."""
3954
+ inclusive_interval = make_inclusive (interval [0 ], interval [1 ])
3955
+ if snapshot .model .interval_unit .is_date_granularity :
3956
+ return f"{ to_ds (inclusive_interval [0 ])} - { to_ds (inclusive_interval [1 ])} "
3957
+
3958
+ if inclusive_interval [0 ].date () == inclusive_interval [1 ].date ():
3959
+ # omit end date if interval start/end on same day
3960
+ return f"{ to_ds (inclusive_interval [0 ])} { inclusive_interval [0 ].strftime ('%H:%M:%S' )} -{ inclusive_interval [1 ].strftime ('%H:%M:%S' )} "
3961
+
3962
+ return f"{ inclusive_interval [0 ].strftime ('%Y-%m-%d %H:%M:%S' )} - { inclusive_interval [1 ].strftime ('%Y-%m-%d %H:%M:%S' )} "
3963
+
3964
+
3965
+ def _format_signal_interval (snapshot : Snapshot , interval : Interval ) -> str :
3966
+ """Format an interval for signal output (without 'insert' prefix)."""
3967
+ return _format_interval (snapshot , interval )
3968
+
3969
+
3813
3970
def _format_evaluation_model_interval (snapshot : Snapshot , interval : Interval ) -> str :
3971
+ """Format an interval for evaluation output (with 'insert' prefix)."""
3814
3972
if snapshot .is_model and (
3815
3973
snapshot .model .kind .is_incremental
3816
3974
or snapshot .model .kind .is_managed
3817
3975
or snapshot .model .kind .is_custom
3818
3976
):
3819
- inclusive_interval = make_inclusive (interval [0 ], interval [1 ])
3820
- 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' )} "
3977
+ formatted_interval = _format_interval (snapshot , interval )
3978
+ return f"insert { formatted_interval } "
3979
+
3826
3980
return ""
3827
3981
3828
3982
0 commit comments