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,118 @@ 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 ,
291+ threads_task_dirs ,
292+ perf_stats ,
293+ cfg ,
294+ eff_num_proc ,
295+ deadlines_cfg ,
296+ )
297+ processes_rows = _build_rows_for_task_types (
298+ task_types_processes ,
299+ processes_task_dirs ,
300+ perf_stats ,
301+ cfg ,
302+ eff_num_proc ,
303+ deadlines_cfg ,
304+ )
225305
226306 parser = argparse .ArgumentParser (description = "Generate HTML scoreboard." )
227307 parser .add_argument (
228- "-o" , "--output" , type = str , required = True , help = "Output file path"
308+ "-o" , "--output" , type = str , required = True , help = "Output directory path"
229309 )
230310 args = parser .parse_args ()
231311
232312 output_path = Path (args .output )
233313 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 )
237314
315+ # Render tables
316+ table_template = env .get_template ("index.html.j2" )
317+ threads_html = table_template .render (
318+ task_types = task_types_threads , rows = threads_rows
319+ )
320+ processes_html = table_template .render (
321+ task_types = task_types_processes , rows = processes_rows
322+ )
323+
324+ with open (output_path / "threads.html" , "w" ) as f :
325+ f .write (threads_html )
326+ with open (output_path / "processes.html" , "w" ) as f :
327+ f .write (processes_html )
328+
329+ # Render index menu page
330+ try :
331+ menu_template = env .get_template ("menu_index.html.j2" )
332+ except Exception :
333+ # Simple fallback menu if template missing
334+ menu_html_content = (
335+ '<html><head><title>Scoreboard</title><link rel="stylesheet" '
336+ 'type="text/css" href="static/main.css"></head><body>'
337+ "<h1>Scoreboard</h1>"
338+ "<ul>"
339+ '<li><a href="threads.html">Threads Scoreboard</a></li>'
340+ '<li><a href="processes.html">Processes Scoreboard</a></li>'
341+ "</ul></body></html>"
342+ )
343+ else :
344+ menu_html_content = menu_template .render (
345+ pages = [
346+ {"href" : "threads.html" , "title" : "Threads Scoreboard" },
347+ {"href" : "processes.html" , "title" : "Processes Scoreboard" },
348+ ]
349+ )
350+
351+ with open (output_path / "index.html" , "w" ) as f :
352+ f .write (menu_html_content )
353+
354+ # Copy static assets
238355 static_src = script_dir / "static"
239356 static_dst = output_path / "static"
240357 if static_src .exists ():
@@ -245,7 +362,10 @@ def main():
245362 else :
246363 logger .warning ("Static directory not found at %s" , static_src )
247364
248- logger .info ("HTML page generated at %s" , output_file )
365+ logger .info (
366+ "HTML pages generated at %s (index.html, threads.html, processes.html)" ,
367+ output_path ,
368+ )
249369
250370
251371if __name__ == "__main__" :
0 commit comments