1515from typing import Any
1616
1717from rich .panel import Panel
18+ from rich .table import Table
1819
1920from aieng_platform_onboard .utils import (
2021 check_onboarded_status ,
2122 console ,
2223 create_env_file ,
2324 fetch_token_from_service ,
25+ get_all_participants_with_status ,
2426 get_github_user ,
2527 get_global_keys ,
2628 get_participant_data ,
2729 get_team_data ,
30+ initialize_firestore_admin ,
2831 initialize_firestore_with_token ,
2932 update_onboarded_status ,
3033 validate_env_file ,
@@ -258,6 +261,121 @@ def _setup_environment(
258261 return output_path
259262
260263
264+ def display_onboarding_status_report (gcp_project : str ) -> int :
265+ """
266+ Display onboarding status report for all participants.
267+
268+ This function is for admin use only and requires proper GCP service
269+ account credentials. It fetches all participants from Firestore and
270+ displays their onboarding status in a table.
271+
272+ Parameters
273+ ----------
274+ gcp_project : str
275+ GCP project ID.
276+
277+ Returns
278+ -------
279+ int
280+ Exit code (0 for success, 1 for failure).
281+ """
282+ console .print (
283+ Panel .fit (
284+ "[bold cyan]Onboarding Status Report[/bold cyan]\n "
285+ "Admin view of all participant onboarding status" ,
286+ border_style = "cyan" ,
287+ )
288+ )
289+
290+ # Initialize Firestore with admin credentials
291+ console .print ("\n [cyan]Connecting to Firestore with admin credentials...[/cyan]" )
292+ try :
293+ db = initialize_firestore_admin (project_id = gcp_project )
294+ console .print ("[green]✓ Connected to Firestore[/green]\n " )
295+ except Exception as e :
296+ console .print (
297+ f"[red]✗ Failed to connect to Firestore:[/red]\n "
298+ f" { e } \n \n "
299+ "[yellow]This command requires admin (service account) credentials.[/yellow]\n "
300+ "[dim]Ensure you are authenticated with proper GCP permissions:[/dim]\n "
301+ " gcloud auth application-default login\n "
302+ " [dim]or have GOOGLE_APPLICATION_CREDENTIALS set[/dim]"
303+ )
304+ return 1
305+
306+ # Fetch all participants
307+ console .print ("[cyan]Fetching participant data...[/cyan]" )
308+ try :
309+ participants = get_all_participants_with_status (db )
310+ console .print (f"[green]✓ Found { len (participants )} participants[/green]\n " )
311+ except Exception as e :
312+ console .print (f"[red]✗ Failed to fetch participant data:[/red] { e } " )
313+ return 1
314+
315+ if not participants :
316+ console .print (
317+ Panel .fit (
318+ "[yellow]No participants found in Firestore[/yellow]\n \n "
319+ "[dim]Use admin scripts to add participants first[/dim]" ,
320+ border_style = "yellow" ,
321+ )
322+ )
323+ return 0
324+
325+ # Create and display status table
326+ table = Table (
327+ title = "Participant Onboarding Status" ,
328+ show_header = True ,
329+ header_style = "bold cyan" ,
330+ show_lines = True ,
331+ )
332+ table .add_column ("GitHub Handle" , style = "yellow" , no_wrap = True )
333+ table .add_column ("Team Name" , style = "blue" )
334+ table .add_column ("Status" , justify = "center" )
335+
336+ # Count onboarded vs total
337+ onboarded_count = 0
338+
339+ for participant in participants :
340+ github_handle = participant ["github_handle" ]
341+ team_name = participant ["team_name" ]
342+ is_onboarded = participant ["onboarded" ]
343+
344+ if is_onboarded :
345+ onboarded_count += 1
346+ status = "[green]✓ Onboarded[/green]"
347+ else :
348+ status = "[red]✗ Not Onboarded[/red]"
349+
350+ table .add_row (github_handle , team_name , status )
351+
352+ console .print (table )
353+ console .print ()
354+
355+ # Display summary
356+ total_count = len (participants )
357+ not_onboarded_count = total_count - onboarded_count
358+ percentage = (onboarded_count / total_count * 100 ) if total_count > 0 else 0
359+
360+ summary_text = (
361+ f"[bold]Onboarding Summary[/bold]\n \n "
362+ f"Total Participants: [cyan]{ total_count } [/cyan]\n "
363+ f"Onboarded: [green]{ onboarded_count } [/green]\n "
364+ f"Not Onboarded: [red]{ not_onboarded_count } [/red]\n "
365+ f"Completion Rate: [yellow]{ percentage :.1f} %[/yellow]"
366+ )
367+
368+ console .print (
369+ Panel .fit (
370+ summary_text ,
371+ border_style = "cyan" ,
372+ title = "Summary" ,
373+ )
374+ )
375+
376+ return 0
377+
378+
261379def _run_tests_and_finalize (
262380 db : Any , github_user : str , skip_test : bool , test_script : str
263381) -> bool :
@@ -340,7 +458,6 @@ def main() -> int: # noqa: PLR0911
340458 parser .add_argument (
341459 "--bootcamp-name" ,
342460 type = str ,
343- required = True ,
344461 help = "Name of the bootcamp (e.g., fall-2025)" ,
345462 )
346463 parser .add_argument (
@@ -363,7 +480,6 @@ def main() -> int: # noqa: PLR0911
363480 parser .add_argument (
364481 "--test-script" ,
365482 type = str ,
366- required = True ,
367483 help = "Path to integration test script" ,
368484 )
369485 parser .add_argument (
@@ -376,9 +492,24 @@ def main() -> int: # noqa: PLR0911
376492 action = "store_true" ,
377493 help = "Force re-onboarding even if already onboarded" ,
378494 )
495+ parser .add_argument (
496+ "--admin-status-report" ,
497+ action = "store_true" ,
498+ help = "Display onboarding status for all participants (admin only, requires service account credentials)" ,
499+ )
379500
380501 args = parser .parse_args ()
381502
503+ # Handle admin status report (early return)
504+ if args .admin_status_report :
505+ return display_onboarding_status_report (args .gcp_project )
506+
507+ # Validate required arguments for normal onboarding flow
508+ if not args .bootcamp_name :
509+ parser .error ("--bootcamp-name is required for participant onboarding" )
510+ if not args .test_script :
511+ parser .error ("--test-script is required for participant onboarding" )
512+
382513 # Print header
383514 console .print (
384515 Panel .fit (
0 commit comments