1717from rich .table import Table
1818
1919
20+ class Dependency :
21+ """A class to manage the display of dependency analysis in the console."""
22+
23+ def __init__ (self ) -> None :
24+ """Initialize the Dependency instance with default values and tables."""
25+ self .description_table = Table (show_header = False , box = None )
26+ self .description_table_content : dict [str , str | Status ] = {
27+ "Package URL:" : Status ("[green]Processing[/]" ),
28+ "Local Cloned Path:" : Status ("[green]Processing[/]" ),
29+ "Remote Path:" : Status ("[green]Processing[/]" ),
30+ "Branch:" : Status ("[green]Processing[/]" ),
31+ "Commit Hash:" : Status ("[green]Processing[/]" ),
32+ "Commit Date:" : Status ("[green]Processing[/]" ),
33+ "CI Services:" : Status ("[green]Processing[/]" ),
34+ "Build Tools:" : Status ("[green]Processing[/]" ),
35+ }
36+ self .progress = Progress (
37+ TextColumn (" RUNNING ANALYSIS" ),
38+ BarColumn (bar_width = None , complete_style = "green" ),
39+ MofNCompleteColumn (),
40+ )
41+ self .task_id : TaskID
42+ self .progress_table = Table (show_header = False , box = None )
43+ self .checks : dict [str , str ] = {}
44+ self .failed_checks_table = Table (show_header = False , box = None )
45+ self .summary_table = Table (show_header = False , box = None )
46+
47+ def add_description_table_content (self , key : str , value : str | Status ) -> None :
48+ """
49+ Add or update a key-value pair in the description table.
50+
51+ Parameters
52+ ----------
53+ key : str
54+ The key to be added or updated.
55+ value : str or Status
56+ The value associated with the key.
57+ """
58+ self .description_table_content [key ] = value
59+ description_table = Table (show_header = False , box = None )
60+ description_table .add_column ("Details" , justify = "left" )
61+ description_table .add_column ("Value" , justify = "left" )
62+ for field , content in self .description_table_content .items ():
63+ description_table .add_row (field , content )
64+
65+ self .description_table = description_table
66+
67+ def no_of_checks (self , value : int ) -> None :
68+ """
69+ Initialize the progress bar with the total number of checks.
70+
71+ Parameters
72+ ----------
73+ value : int
74+ The total number of checks to be performed.
75+ """
76+ self .task_id = self .progress .add_task ("analyzing" , total = value )
77+
78+ def remove_progress_bar (self ) -> None :
79+ """Remove the progress bar from the display."""
80+ self .progress .remove_task (self .task_id )
81+
82+ def update_checks (self , check_id : str , status : str = "RUNNING" ) -> None :
83+ """
84+ Update the status of a specific check and refresh the progress table.
85+
86+ Parameters
87+ ----------
88+ check_id : str
89+ The identifier of the check to be updated.
90+ status : str, optional
91+ The new status of the check, by default "RUNNING"
92+ """
93+ self .checks [check_id ] = status
94+
95+ progress_table = Table (show_header = False , box = None )
96+ progress_table .add_column ("Status" , justify = "left" )
97+ progress_table .add_column ("Check" , justify = "left" )
98+
99+ for check_name , check_status in self .checks .items ():
100+ if check_status == "RUNNING" :
101+ progress_table .add_row (Status ("[bold green]RUNNING[/]" ), check_name )
102+ self .progress_table = progress_table
103+
104+ if self .task_id is not None and status != "RUNNING" :
105+ self .progress .update (self .task_id , advance = 1 )
106+
107+ def update_checks_summary (self , checks_summary : dict , total_checks : int ) -> None :
108+ """
109+ Update the summary tables with the results of the checks.
110+
111+ Parameters
112+ ----------
113+ checks_summary : dict
114+ Dictionary containing lists of checks categorized by their results.
115+ total_checks : int
116+ The total number of checks.
117+ """
118+ failed_checks_table = Table (show_header = False , box = None )
119+ failed_checks_table .add_column ("Status" , justify = "left" )
120+ failed_checks_table .add_column ("Check ID" , justify = "left" )
121+ failed_checks_table .add_column ("Description" , justify = "left" )
122+
123+ failed_checks = checks_summary ["FAILED" ]
124+ for check in failed_checks :
125+ failed_checks_table .add_row (
126+ "[bold red]FAILED[/]" ,
127+ check .check .check_id ,
128+ check .check .check_description ,
129+ )
130+
131+ self .failed_checks_table = failed_checks_table
132+
133+ summary_table = Table (show_header = False , box = None )
134+ summary_table .add_column ("Check Result Type" , justify = "left" )
135+ summary_table .add_column ("Count" , justify = "left" )
136+ summary_table .add_row ("Total Checks" , str (total_checks ), style = "white" )
137+
138+ for check_result_type , checks in checks_summary .items ():
139+ if check_result_type == "PASSED" :
140+ summary_table .add_row ("PASSED" , str (len (checks )), style = "green" )
141+ if check_result_type == "FAILED" :
142+ summary_table .add_row ("FAILED" , str (len (checks )), style = "red" )
143+ if check_result_type == "SKIPPED" :
144+ summary_table .add_row ("SKIPPED" , str (len (checks )), style = "yellow" )
145+ if check_result_type == "DISABLED" :
146+ summary_table .add_row ("DISABLED" , str (len (checks )), style = "bright_blue" )
147+ if check_result_type == "UNKNOWN" :
148+ summary_table .add_row ("UNKNOWN" , str (len (checks )), style = "white" )
149+
150+ self .summary_table = summary_table
151+
152+ def make_layout (self ) -> list [RenderableType ]:
153+ """
154+ Create the layout for the live console display.
155+
156+ Returns
157+ -------
158+ list[RenderableType]
159+ A list of rich RenderableType objects containing the layout for the live console display.
160+ """
161+ layout : list [RenderableType ] = []
162+ if self .description_table .row_count > 0 :
163+ layout = layout + [
164+ "" ,
165+ self .description_table ,
166+ ]
167+ if self .progress_table .row_count > 0 :
168+ layout = layout + ["" , self .progress , "" , self .progress_table ]
169+ if self .failed_checks_table .row_count > 0 :
170+ layout = layout + [
171+ "" ,
172+ Rule (" SUMMARY" , align = "left" ),
173+ "" ,
174+ self .failed_checks_table ,
175+ ]
176+ if self .summary_table .row_count > 0 :
177+ layout = layout + ["" , self .summary_table ]
178+ elif self .summary_table .row_count > 0 :
179+ layout = layout + [
180+ "" ,
181+ Rule (" SUMMARY" , align = "left" ),
182+ "" ,
183+ self .summary_table ,
184+ ]
185+ return layout
186+
187+
20188class RichConsoleHandler (RichHandler ):
21189 """A rich console handler for logging with rich formatting and live updates."""
22190
@@ -67,6 +235,8 @@ def __init__(self, *args: Any, verbose: bool = False, **kwargs: Any) -> None:
67235 "Dependencies Report" : "Not Generated" ,
68236 "JSON Report" : "Not Generated" ,
69237 }
238+ self .if_dependency : bool = False
239+ self .dependency_analysis_list : list [Dependency ] = []
70240 self .components_violates_table = Table (box = None )
71241 self .components_satisfy_table = Table (box = None )
72242 self .policy_summary_table = Table (show_header = False , box = None )
@@ -138,6 +308,11 @@ def add_description_table_content(self, key: str, value: str | Status) -> None:
138308 value : str or Status
139309 The value associated with the key.
140310 """
311+ if self .if_dependency and self .dependency_analysis_list :
312+ dependency = self .dependency_analysis_list [- 1 ]
313+ dependency .add_description_table_content (key , value )
314+ return
315+
141316 self .description_table_content [key ] = value
142317 description_table = Table (show_header = False , box = None )
143318 description_table .add_column ("Details" , justify = "left" )
@@ -156,8 +331,20 @@ def no_of_checks(self, value: int) -> None:
156331 value : int
157332 The total number of checks to be performed.
158333 """
334+ if self .if_dependency and self .dependency_analysis_list :
335+ dependency = self .dependency_analysis_list [- 1 ]
336+ dependency .no_of_checks (value )
337+ return
159338 self .task_id = self .progress .add_task ("analyzing" , total = value )
160339
340+ def remove_progress_bar (self ) -> None :
341+ """Remove the progress bar from the display."""
342+ if self .if_dependency and self .dependency_analysis_list :
343+ dependency = self .dependency_analysis_list [- 1 ]
344+ dependency .remove_progress_bar ()
345+ return
346+ self .progress .remove_task (self .task_id )
347+
161348 def update_checks (self , check_id : str , status : str = "RUNNING" ) -> None :
162349 """
163350 Update the status of a specific check and refresh the progress table.
@@ -169,6 +356,10 @@ def update_checks(self, check_id: str, status: str = "RUNNING") -> None:
169356 status : str, optional
170357 The new status of the check, by default "RUNNING"
171358 """
359+ if self .if_dependency and self .dependency_analysis_list :
360+ dependency = self .dependency_analysis_list [- 1 ]
361+ dependency .update_checks (check_id , status )
362+ return
172363 self .checks [check_id ] = status
173364
174365 progress_table = Table (show_header = False , box = None )
@@ -194,6 +385,10 @@ def update_checks_summary(self, checks_summary: dict, total_checks: int) -> None
194385 total_checks : int
195386 The total number of checks.
196387 """
388+ if self .if_dependency and self .dependency_analysis_list :
389+ dependency = self .dependency_analysis_list [- 1 ]
390+ dependency .update_checks_summary (checks_summary , total_checks )
391+ return
197392 failed_checks_table = Table (show_header = False , box = None )
198393 failed_checks_table .add_column ("Status" , justify = "left" )
199394 failed_checks_table .add_column ("Check ID" , justify = "left" )
@@ -249,6 +444,20 @@ def update_report_table(self, report_type: str, report_path: str) -> None:
249444
250445 self .report_table = report_table
251446
447+ def is_dependency (self , value : bool ) -> None :
448+ """
449+ Update the flag indicating whether the analyzed package is a dependency.
450+
451+ Parameters
452+ ----------
453+ value : bool
454+ True if the package is a dependency, False otherwise.
455+ """
456+ self .if_dependency = value
457+ if self .if_dependency :
458+ dependency = Dependency ()
459+ self .dependency_analysis_list .append (dependency )
460+
252461 def generate_policy_summary_table (self ) -> None :
253462 """Generate the policy summary table based on the current policy summary data."""
254463 policy_summary_table = Table (show_header = False , box = None )
@@ -398,7 +607,11 @@ def make_layout(self) -> Group:
398607 layout = layout + [error_log_panel ]
399608 if self .command == "analyze" :
400609 if self .description_table .row_count > 0 :
401- layout = layout + [Rule (" DESCRIPTION" , align = "left" ), "" , self .description_table ]
610+ layout = layout + [
611+ Rule (" DESCRIPTION" , align = "left" ),
612+ "" ,
613+ self .description_table ,
614+ ]
402615 if self .progress_table .row_count > 0 :
403616 layout = layout + ["" , self .progress , "" , self .progress_table ]
404617 if self .failed_checks_table .row_count > 0 :
@@ -425,6 +638,17 @@ def make_layout(self) -> Group:
425638 layout = layout + [
426639 self .report_table ,
427640 ]
641+ if self .if_dependency and self .dependency_analysis_list :
642+ for idx , dependency in enumerate (self .dependency_analysis_list , start = 1 ):
643+ dependency_layout = dependency .make_layout ()
644+ layout = (
645+ layout
646+ + [
647+ "" ,
648+ Rule (f" DEPENDENCY { idx } " , align = "left" ),
649+ ]
650+ + dependency_layout
651+ )
428652 elif self .command == "verify-policy" :
429653 if self .policy_summary_table .row_count > 0 :
430654 if self .components_satisfy_table .row_count > 0 :
0 commit comments