@@ -918,163 +918,165 @@ def _render_chart(self) -> None:
918918 class ValidationApp (App ):
919919 """Interactive validation results viewer."""
920920
921- CSS = """
922- Screen {
923- background: $surface;
924- }
925-
926- .section {
927- margin: 1 2;
928- padding: 1;
929- border: solid $primary;
930- }
931-
932- .section-title {
933- text-style: bold;
934- margin-bottom: 1;
935- }
936-
937- .error {
938- color: $error;
939- }
921+ CSS = """
922+ Screen {
923+ background: $surface;
924+ }
940925
941- .warning {
942- color: $warning;
943- }
926+ .section {
927+ margin: 1 2;
928+ padding: 1;
929+ border: solid $primary;
930+ }
944931
945- .success {
946- color: $success;
947- }
932+ .section-title {
933+ text-style: bold;
934+ margin-bottom: 1;
935+ }
948936
949- .info {
950- color: $accent;
951- }
952- """
937+ .error {
938+ color: $error;
939+ }
953940
954- BINDINGS = [
955- Binding ( "ctrl+c" , "quit" , "Quit" ),
956- ]
941+ .warning {
942+ color: $warning;
943+ }
957944
958- def __init__ (self , directory : Path , verbose : bool = False ):
959- super ().__init__ ()
960- self .directory = directory
961- self .verbose = verbose
962- self .errors = []
963- self .warnings = []
964- self .info = []
945+ .success {
946+ color: $success;
947+ }
965948
966- def compose (self ) -> ComposeResult :
967- """Create child widgets."""
968- yield Header ()
969- with VerticalScroll ():
970- yield Static ("" , id = "validation-results" )
971- yield Footer ()
949+ .info {
950+ color: $accent;
951+ }
952+ """
953+
954+ BINDINGS = [
955+ Binding ("ctrl+c" , "quit" , "Quit" ),
956+ ]
957+
958+ def __init__ (self , directory : Path , verbose : bool = False ):
959+ super ().__init__ ()
960+ self .directory = directory
961+ self .verbose = verbose
962+ self .errors = []
963+ self .warnings = []
964+ self .info = []
965+
966+ def compose (self ) -> ComposeResult :
967+ """Create child widgets."""
968+ yield Header ()
969+ with VerticalScroll ():
970+ yield Static ("" , id = "validation-results" )
971+ yield Footer ()
972+
973+ def on_mount (self ) -> None :
974+ """Run validation."""
975+ try :
976+ layer = SemanticLayer ()
977+ load_from_directory (layer , str (self .directory ))
972978
973- def on_mount (self ) -> None :
974- """Run validation."""
975- try :
976- layer = SemanticLayer ()
977- load_from_directory (layer , str (self .directory ))
979+ if not layer .graph .models :
980+ self .exit (message = "No models found in directory" )
981+ return
978982
979- if not layer .graph .models :
980- self .exit (message = "No models found in directory" )
981- return
983+ self .info .append (f"Loaded { len (layer .graph .models )} models" )
982984
983- self .info .append (f"Loaded { len (layer .graph .models )} models" )
984-
985- # Validate each model
986- for model_name , model in layer .graph .models .items ():
987- # Check primary key
988- if not model .primary_key :
989- self .warnings .append (f"Model '{ model_name } ' has no primary key defined" )
990-
991- # Check for dimensions
992- if not model .dimensions :
993- self .warnings .append (f"Model '{ model_name } ' has no dimensions" )
994-
995- # Check for metrics
996- if not model .metrics :
997- self .warnings .append (f"Model '{ model_name } ' has no metrics" )
998-
999- # Validate relationships
1000- for rel in model .relationships :
1001- if rel .name not in layer .graph .models :
1002- self .errors .append (f"Model '{ model_name } ' has relationship to '{ rel .name } ' which doesn't exist" )
1003-
1004- # Check for duplicate dimension names
1005- dim_names = [d .name for d in model .dimensions ]
1006- duplicates = [name for name in set (dim_names ) if dim_names .count (name ) > 1 ]
1007- if duplicates :
1008- self .errors .append (f"Model '{ model_name } ' has duplicate dimensions: { ', ' .join (duplicates )} " )
1009-
1010- # Check for duplicate metric names
1011- metric_names = [m .name for m in model .metrics ]
1012- duplicates = [name for name in set (metric_names ) if metric_names .count (name ) > 1 ]
1013- if duplicates :
1014- self .errors .append (f"Model '{ model_name } ' has duplicate metrics: { ', ' .join (duplicates )} " )
1015-
1016- # Check for orphaned models
1017- if len (layer .graph .models ) > 1 :
1018- orphaned = []
985+ # Validate each model
1019986 for model_name , model in layer .graph .models .items ():
1020- has_outgoing = len (model .relationships ) > 0
1021- has_incoming = any (
1022- any (r .name == model_name for r in m .relationships )
1023- for name , m in layer .graph .models .items ()
1024- if name != model_name
1025- )
1026- if not has_outgoing and not has_incoming :
1027- orphaned .append (model_name )
1028-
1029- if orphaned :
1030- self .warnings .append (f"Orphaned models (no relationships): { ', ' .join (orphaned )} " )
987+ # Check primary key
988+ if not model .primary_key :
989+ self .warnings .append (f"Model '{ model_name } ' has no primary key defined" )
1031990
1032- # Add summary stats
1033- total_dims = sum (len (m .dimensions ) for m in layer .graph .models .values ())
1034- total_metrics = sum (len (m .metrics ) for m in layer .graph .models .values ())
1035- total_rels = sum (len (m .relationships ) for m in layer .graph .models .values ())
991+ # Check for dimensions
992+ if not model .dimensions :
993+ self .warnings .append (f"Model '{ model_name } ' has no dimensions" )
1036994
1037- self . info . append ( f"Total dimensions: { total_dims } " )
1038- self . info . append ( f"Total metrics: { total_metrics } " )
1039- self .info .append (f"Total relationships: { total_rels } " )
995+ # Check for metrics
996+ if not model . metrics :
997+ self .warnings .append (f"Model ' { model_name } ' has no metrics " )
1040998
1041- # Display results
1042- self ._update_display ()
999+ # Validate relationships
1000+ for rel in model .relationships :
1001+ if rel .name not in layer .graph .models :
1002+ self .errors .append (
1003+ f"Model '{ model_name } ' has relationship to '{ rel .name } ' which doesn't exist"
1004+ )
10431005
1044- except Exception as e :
1045- self .exit (message = f"Error during validation: { e } " )
1046-
1047- def _update_display (self ) -> None :
1048- """Update the validation results display."""
1049- results = self .query_one ("#validation-results" , Static )
1050- content = []
1051-
1052- content .append (f"[bold]Validation Results: { self .directory } [/bold]\n " )
1053-
1054- if self .errors :
1055- content .append ("[bold error]✗ Errors[/bold error]" )
1056- for error in self .errors :
1057- content .append (f" [error]✗[/error] { error } " )
1058- content .append ("" )
1059-
1060- if self .warnings :
1061- content .append ("[bold warning]⚠ Warnings[/bold warning]" )
1062- for warning in self .warnings :
1063- content .append (f" [warning]⚠[/warning] { warning } " )
1064- content .append ("" )
1065-
1066- if self .verbose or not (self .errors or self .warnings ):
1067- content .append ("[bold info]ℹ Info[/bold info]" )
1068- for i in self .info :
1069- content .append (f" [info]ℹ[/info] { i } " )
1070- content .append ("" )
1071-
1072- if not self .errors :
1073- content .append ("\n [bold success]✓ Validation Passed[/bold success]" )
1074- else :
1075- content .append ("\n [bold error]✗ Validation Failed[/bold error]" )
1006+ # Check for duplicate dimension names
1007+ dim_names = [d .name for d in model .dimensions ]
1008+ duplicates = [name for name in set (dim_names ) if dim_names .count (name ) > 1 ]
1009+ if duplicates :
1010+ self .errors .append (f"Model '{ model_name } ' has duplicate dimensions: { ', ' .join (duplicates )} " )
1011+
1012+ # Check for duplicate metric names
1013+ metric_names = [m .name for m in model .metrics ]
1014+ duplicates = [name for name in set (metric_names ) if metric_names .count (name ) > 1 ]
1015+ if duplicates :
1016+ self .errors .append (f"Model '{ model_name } ' has duplicate metrics: { ', ' .join (duplicates )} " )
1017+
1018+ # Check for orphaned models
1019+ if len (layer .graph .models ) > 1 :
1020+ orphaned = []
1021+ for model_name , model in layer .graph .models .items ():
1022+ has_outgoing = len (model .relationships ) > 0
1023+ has_incoming = any (
1024+ any (r .name == model_name for r in m .relationships )
1025+ for name , m in layer .graph .models .items ()
1026+ if name != model_name
1027+ )
1028+ if not has_outgoing and not has_incoming :
1029+ orphaned .append (model_name )
1030+
1031+ if orphaned :
1032+ self .warnings .append (f"Orphaned models (no relationships): { ', ' .join (orphaned )} " )
1033+
1034+ # Add summary stats
1035+ total_dims = sum (len (m .dimensions ) for m in layer .graph .models .values ())
1036+ total_metrics = sum (len (m .metrics ) for m in layer .graph .models .values ())
1037+ total_rels = sum (len (m .relationships ) for m in layer .graph .models .values ())
1038+
1039+ self .info .append (f"Total dimensions: { total_dims } " )
1040+ self .info .append (f"Total metrics: { total_metrics } " )
1041+ self .info .append (f"Total relationships: { total_rels } " )
1042+
1043+ # Display results
1044+ self ._update_display ()
1045+
1046+ except Exception as e :
1047+ self .exit (message = f"Error during validation: { e } " )
1048+
1049+ def _update_display (self ) -> None :
1050+ """Update the validation results display."""
1051+ results = self .query_one ("#validation-results" , Static )
1052+ content = []
1053+
1054+ content .append (f"[bold]Validation Results: { self .directory } [/bold]\n " )
1055+
1056+ if self .errors :
1057+ content .append ("[bold error]✗ Errors[/bold error]" )
1058+ for error in self .errors :
1059+ content .append (f" [error]✗[/error] { error } " )
1060+ content .append ("" )
1061+
1062+ if self .warnings :
1063+ content .append ("[bold warning]⚠ Warnings[/bold warning]" )
1064+ for warning in self .warnings :
1065+ content .append (f" [warning]⚠[/warning] { warning } " )
1066+ content .append ("" )
1067+
1068+ if self .verbose or not (self .errors or self .warnings ):
1069+ content .append ("[bold info]ℹ Info[/bold info]" )
1070+ for i in self .info :
1071+ content .append (f" [info]ℹ[/info] { i } " )
1072+ content .append ("" )
1073+
1074+ if not self .errors :
1075+ content .append ("\n [bold success]✓ Validation Passed[/bold success]" )
1076+ else :
1077+ content .append ("\n [bold error]✗ Validation Failed[/bold error]" )
10761078
1077- results .update ("\n " .join (content ))
1079+ results .update ("\n " .join (content ))
10781080
10791081
10801082@app .command ()
0 commit comments