1313logger = logging .getLogger (__name__ )
1414
1515task_types = ["all" , "mpi" , "omp" , "seq" , "stl" , "tbb" ]
16+ task_types_threads = ["all" , "omp" , "seq" , "stl" , "tbb" ]
17+ task_types_processes = ["mpi" , "seq" ]
1618
1719script_dir = Path (__file__ ).parent
1820tasks_dir = script_dir .parent / "tasks"
1921
2022
23+ def _read_tasks_type (task_dir : Path ) -> str | None :
24+ """Read tasks_type from settings.json in the task directory (if present)."""
25+ settings_path = task_dir / "settings.json"
26+ if settings_path .exists ():
27+ try :
28+ import json
29+
30+ with open (settings_path , "r" ) as f :
31+ data = json .load (f )
32+ return data .get ("tasks_type" ) # "threads" or "processes"
33+ except Exception as e :
34+ logger .warning ("Failed to parse %s: %s" , settings_path , e )
35+ return None
36+
37+
2138def discover_tasks (tasks_dir , task_types ):
22- """Discover tasks and their implementation status from the filesystem."""
39+ """Discover tasks and their implementation status from the filesystem.
40+
41+ Returns:
42+ directories: dict[task_name][task_type] -> status
43+ tasks_type_map: dict[task_name] -> "threads" | "processes" | None
44+ """
2345 directories = defaultdict (dict )
46+ tasks_type_map : dict [str , str | None ] = {}
2447
2548 if tasks_dir .exists () and tasks_dir .is_dir ():
2649 for task_name_dir in tasks_dir .iterdir ():
2750 if task_name_dir .is_dir () and task_name_dir .name not in ["common" ]:
2851 task_name = task_name_dir .name
52+ # Save tasks_type from settings.json if present
53+ tasks_type_map [task_name ] = _read_tasks_type (task_name_dir )
2954 for task_type in task_types :
3055 task_type_dir = task_name_dir / task_type
3156 if task_type_dir .exists () and task_type_dir .is_dir ():
@@ -35,10 +60,10 @@ def discover_tasks(tasks_dir, task_types):
3560 else :
3661 directories [task_name ][task_type ] = "done"
3762
38- return directories
63+ return directories , tasks_type_map
3964
4065
41- directories = discover_tasks (tasks_dir , task_types )
66+ directories , tasks_type_map = discover_tasks (tasks_dir , task_types )
4267
4368
4469def load_performance_data (perf_stat_file_path ):
@@ -163,24 +188,20 @@ def load_configurations():
163188 return cfg , eff_num_proc , deadlines_cfg , plagiarism_cfg
164189
165190
166- def main ():
167- """Main function to generate the scoreboard."""
168- cfg , eff_num_proc , deadlines_cfg , plagiarism_cfg = load_configurations ()
169-
170- env = Environment (loader = FileSystemLoader (Path (__file__ ).parent / "templates" ))
171-
172- perf_stat_file_path = (
173- script_dir .parent / "build" / "perf_stat_dir" / "task_run_perf_table.csv"
174- )
175-
176- # Read and parse performance statistics CSV
177- perf_stats = load_performance_data (perf_stat_file_path )
178-
191+ def _build_rows_for_task_types (
192+ selected_task_types : list [str ],
193+ dir_names : list [str ],
194+ perf_stats : dict ,
195+ cfg ,
196+ eff_num_proc ,
197+ deadlines_cfg ,
198+ ):
199+ """Build rows for the given list of task directories and selected task types."""
179200 rows = []
180- for dir in sorted (directories . keys () ):
201+ for dir in sorted (dir_names ):
181202 row_types = []
182203 total_count = 0
183- for task_type in task_types :
204+ for task_type in selected_task_types :
184205 status = directories [dir ].get (task_type )
185206 sol_points , solution_style = get_solution_points_and_style (
186207 task_type , status , cfg
@@ -219,22 +240,113 @@ def main():
219240 total_count += task_points
220241
221242 rows .append ({"task" : dir , "types" : row_types , "total" : total_count })
243+ return rows
244+
245+
246+ def main ():
247+ """Main function to generate the scoreboard.
248+
249+ Now generates three pages in the output dir:
250+ - index.html: simple menu linking to threads.html and processes.html
251+ - threads.html: scoreboard for thread-based tasks
252+ - processes.html: scoreboard for process-based tasks
253+ """
254+ cfg , eff_num_proc , deadlines_cfg , plagiarism_cfg_local = load_configurations ()
222255
223- template = env .get_template ("index.html.j2" )
224- html_content = template .render (task_types = task_types , rows = rows )
256+ # Make plagiarism config available to rows builder
257+ global plagiarism_cfg
258+ plagiarism_cfg = plagiarism_cfg_local
259+
260+ env = Environment (loader = FileSystemLoader (Path (__file__ ).parent / "templates" ))
261+
262+ # Locate perf CSV from CI or local runs
263+ candidates = [
264+ script_dir .parent / "build" / "perf_stat_dir" / "task_run_perf_table.csv" ,
265+ script_dir .parent / "perf_stat_dir" / "task_run_perf_table.csv" ,
266+ ]
267+ perf_stat_file_path = next ((p for p in candidates if p .exists ()), candidates [0 ])
268+
269+ # Read and parse performance statistics CSV
270+ perf_stats = load_performance_data (perf_stat_file_path )
271+
272+ # Partition tasks by tasks_type from settings.json
273+ threads_task_dirs = [
274+ name for name , ttype in tasks_type_map .items () if ttype == "threads"
275+ ]
276+ processes_task_dirs = [
277+ name for name , ttype in tasks_type_map .items () if ttype == "processes"
278+ ]
279+
280+ # Fallback: if settings.json is missing, guess by directory name heuristic
281+ for name in directories .keys ():
282+ if name not in tasks_type_map or tasks_type_map [name ] is None :
283+ if "threads" in name :
284+ threads_task_dirs .append (name )
285+ elif "processes" in name :
286+ processes_task_dirs .append (name )
287+
288+ # Build rows for each page
289+ threads_rows = _build_rows_for_task_types (
290+ task_types_threads , threads_task_dirs , perf_stats , cfg , eff_num_proc , deadlines_cfg
291+ )
292+ processes_rows = _build_rows_for_task_types (
293+ task_types_processes ,
294+ processes_task_dirs ,
295+ perf_stats ,
296+ cfg ,
297+ eff_num_proc ,
298+ deadlines_cfg ,
299+ )
225300
226301 parser = argparse .ArgumentParser (description = "Generate HTML scoreboard." )
227302 parser .add_argument (
228- "-o" , "--output" , type = str , required = True , help = "Output file path"
303+ "-o" , "--output" , type = str , required = True , help = "Output directory path"
229304 )
230305 args = parser .parse_args ()
231306
232307 output_path = Path (args .output )
233308 output_path .mkdir (parents = True , exist_ok = True )
234- output_file = output_path / "index.html"
235- with open (output_file , "w" ) as file :
236- file .write (html_content )
237309
310+ # Render tables
311+ table_template = env .get_template ("index.html.j2" )
312+ threads_html = table_template .render (
313+ task_types = task_types_threads , rows = threads_rows
314+ )
315+ processes_html = table_template .render (
316+ task_types = task_types_processes , rows = processes_rows
317+ )
318+
319+ with open (output_path / "threads.html" , "w" ) as f :
320+ f .write (threads_html )
321+ with open (output_path / "processes.html" , "w" ) as f :
322+ f .write (processes_html )
323+
324+ # Render index menu page
325+ try :
326+ menu_template = env .get_template ("menu_index.html.j2" )
327+ except Exception :
328+ # Simple fallback menu if template missing
329+ menu_html_content = (
330+ "<html><head><title>Scoreboard</title><link rel=\" stylesheet\" "
331+ "type=\" text/css\" href=\" static/main.css\" ></head><body>"
332+ "<h1>Scoreboard</h1>"
333+ "<ul>"
334+ "<li><a href=\" threads.html\" >Threads Scoreboard</a></li>"
335+ "<li><a href=\" processes.html\" >Processes Scoreboard</a></li>"
336+ "</ul></body></html>"
337+ )
338+ else :
339+ menu_html_content = menu_template .render (
340+ pages = [
341+ {"href" : "threads.html" , "title" : "Threads Scoreboard" },
342+ {"href" : "processes.html" , "title" : "Processes Scoreboard" },
343+ ]
344+ )
345+
346+ with open (output_path / "index.html" , "w" ) as f :
347+ f .write (menu_html_content )
348+
349+ # Copy static assets
238350 static_src = script_dir / "static"
239351 static_dst = output_path / "static"
240352 if static_src .exists ():
@@ -245,7 +357,7 @@ def main():
245357 else :
246358 logger .warning ("Static directory not found at %s" , static_src )
247359
248- logger .info ("HTML page generated at %s" , output_file )
360+ logger .info ("HTML pages generated at %s (index.html, threads.html, processes.html) " , output_path )
249361
250362
251363if __name__ == "__main__" :
0 commit comments