@@ -527,6 +527,72 @@ def get_available_trajectories(self) -> list[tuple]:
527527
528528 return sorted (trajectories )
529529
530+ def analyze_line_counts (self ) -> dict [str , Any ]:
531+ """Analyze line counts across all rounds for all files that appear in changed files"""
532+ # Collect all files that appear in any changed files across all rounds and players
533+ all_files = set ()
534+ players_dir = self .log_dir / "players"
535+
536+ if not players_dir .exists ():
537+ return {"all_files" : [], "line_counts_by_round" : {}}
538+
539+ # First pass: collect all files from all changes_r*.json files
540+ for player_dir in players_dir .iterdir ():
541+ if not player_dir .is_dir ():
542+ continue
543+
544+ for changes_file in player_dir .glob ("changes_r*.json" ):
545+ try :
546+ changes_data = json .loads (changes_file .read_text ())
547+ modified_files = changes_data .get ("modified_files" , {})
548+ all_files .update (modified_files .keys ())
549+ except (json .JSONDecodeError , KeyError ):
550+ continue
551+
552+ all_files_list = sorted (list (all_files ))
553+
554+ # Second pass: count lines for each file in each round for each player
555+ line_counts_by_round = {}
556+
557+ for player_dir in players_dir .iterdir ():
558+ if not player_dir .is_dir ():
559+ continue
560+
561+ player_name = player_dir .name
562+
563+ # Get all rounds for this player
564+ changes_files = sorted (player_dir .glob ("changes_r*.json" ), key = lambda x : int (x .stem .split ("_r" )[1 ]))
565+
566+ # Track line counts for this player across rounds
567+ player_line_counts = {}
568+ current_file_lines = {} # Track current state of each file
569+
570+ for changes_file in changes_files :
571+ try :
572+ round_num = int (changes_file .stem .split ("_r" )[1 ])
573+ changes_data = json .loads (changes_file .read_text ())
574+ modified_files = changes_data .get ("modified_files" , {})
575+
576+ # Update line counts for files that changed in this round
577+ for file_path , file_content in modified_files .items ():
578+ if file_content :
579+ current_file_lines [file_path ] = len (file_content .splitlines ())
580+
581+ # Record line counts for all files in this round
582+ round_line_counts = {}
583+ for file_path in all_files_list :
584+ round_line_counts [file_path ] = current_file_lines .get (file_path , 0 )
585+
586+ player_line_counts [round_num ] = round_line_counts
587+
588+ except (json .JSONDecodeError , KeyError , ValueError ):
589+ continue
590+
591+ if player_line_counts :
592+ line_counts_by_round [player_name ] = player_line_counts
593+
594+ return {"all_files" : all_files_list , "line_counts_by_round" : line_counts_by_round }
595+
530596
531597app = Flask (__name__ )
532598
@@ -583,6 +649,9 @@ def index():
583649 if trajectory :
584650 trajectories_by_round [round_num ].append (trajectory )
585651
652+ # Get analysis data
653+ analysis_data = parser .analyze_line_counts ()
654+
586655 # Get the full path of the selected folder
587656 selected_folder_path = str (folder_path )
588657
@@ -592,6 +661,7 @@ def index():
592661 selected_folder_path = selected_folder_path ,
593662 metadata = metadata ,
594663 trajectories_by_round = trajectories_by_round ,
664+ analysis_data = analysis_data ,
595665 )
596666
597667
@@ -887,4 +957,28 @@ def move_folder():
887957 return jsonify ({"success" : False , "error" : str (e )})
888958
889959
960+ @app .route ("/analysis/line-counts" )
961+ def analysis_line_counts ():
962+ """Get line count analysis data for the current game"""
963+ selected_folder = request .args .get ("folder" )
964+
965+ if not selected_folder :
966+ return jsonify ({"success" : False , "error" : "No folder specified" })
967+
968+ # Validate the selected folder exists and is a game folder
969+ logs_dir = LOG_BASE_DIR
970+ folder_path = logs_dir / selected_folder
971+
972+ if not folder_path .exists () or not is_game_folder (folder_path ):
973+ return jsonify ({"success" : False , "error" : "Invalid folder" })
974+
975+ try :
976+ parser = LogParser (folder_path )
977+ analysis_data = parser .analyze_line_counts ()
978+
979+ return jsonify ({"success" : True , "data" : analysis_data })
980+ except Exception as e :
981+ return jsonify ({"success" : False , "error" : str (e )})
982+
983+
890984# Use run_viewer.py to launch the application
0 commit comments