22import logging
33import re
44from pathlib import Path
5- from typing import TYPE_CHECKING
5+ from typing import TYPE_CHECKING , Any
66
77from django .core .management import call_command
88from django .http import FileResponse , Http404 , HttpRequest , JsonResponse
1313from django .views .decorators .csrf import csrf_exempt
1414from django .views .decorators .http import require_http_methods
1515from pydantic_core ._pydantic_core import ValidationError
16- from sqlalchemy import create_engine
16+ from sqlalchemy import Engine , create_engine , select
17+ from sqlalchemy .orm import Session
1718
18- from imgtests .constant import CONFIG_DIR , EXCEL_REPORTS_DIR , REPORTS_DIR
19+ from imgtests .constant import CONFIG_DIR , DISTRIBUTION_DESCRIPTIONS , EXCEL_REPORTS_DIR , REPORTS_DIR
1920from imgtests .database .database import ImgtestsDatabase , PostgresCreds
20- from imgtests .reporting .excel_export import export_database_to_excel
21+ from imgtests .database .models .configuration import ConfigurationBase
22+ from imgtests .reporting .cli import EXPORT_TABLES
23+ from imgtests .reporting .excel_export import distribution_name_from_os , export_database_to_excel
2124from imgtests .reporting .html_report import ReportGenerator
2225from imgtests .runner import get_test_name
2326from imgtests .suites .map import ALL_SUITES
@@ -375,9 +378,21 @@ def __safe_relative_path(*parts: str) -> Path:
375378
376379@csrf_exempt
377380@require_http_methods (["POST" ])
378- def api_export_excel (request : HttpRequest ) -> JsonResponse : # noqa: ARG001
381+ def api_export_excel (request : HttpRequest ) -> JsonResponse :
382+ try :
383+ body = json .loads (request .body ) if request .body else {}
384+ except json .JSONDecodeError :
385+ return JsonResponse ({"error" : "Invalid request body" }, status = 400 )
386+
387+ tables = body .get ("tables" , list (EXPORT_TABLES ))
388+ selected_distributions = body .get ("distributions" )
389+
390+ if not tables :
391+ return JsonResponse ({"error" : "At least one table must be selected" }, status = 400 )
392+
379393 timestamp = timezone .now ().strftime ("%Y%m%d_%H%M%S" )
380394 output_path = EXCEL_REPORTS_DIR / f"export_{ timestamp } .xlsx"
395+
381396 try :
382397 db_creds = PostgresCreds ()
383398 except ValidationError :
@@ -391,10 +406,12 @@ def api_export_excel(request: HttpRequest) -> JsonResponse: # noqa: ARG001
391406
392407 try :
393408 engine = create_engine (db_url )
409+ configuration_ids = _resolve_configuration_ids (engine , selected_distributions )
394410 export_database_to_excel (
395411 engine = engine ,
396412 output_path = output_path ,
397- configuration_ids = {"poky" : 1 , "suse" : 2 },
413+ configuration_ids = configuration_ids ,
414+ tables = tables ,
398415 )
399416 except Exception as err : # noqa: BLE001
400417 return JsonResponse ({"error" : f"Export failed: { err } " }, status = 500 )
@@ -410,10 +427,38 @@ def api_export_excel(request: HttpRequest) -> JsonResponse: # noqa: ARG001
410427 )
411428
412429
430+ def _resolve_configuration_ids (
431+ engine : Engine ,
432+ selected_distributions : list [str ] | None = None ,
433+ ) -> dict [str , int ]:
434+
435+ with Session (engine ) as session :
436+ query = select (ConfigurationBase .config_id , ConfigurationBase .os )
437+ configs = session .execute (query )
438+
439+ # TODO: if a single distro has multiple config ids the first one is selected
440+ all_distros : dict [str , int ] = {}
441+ for config_id , os_name in configs :
442+ distro = distribution_name_from_os (os_name )
443+ if distro and distro not in all_distros :
444+ all_distros [distro ] = config_id
445+
446+ if selected_distributions is not None :
447+ missing = [d for d in selected_distributions if d not in all_distros ]
448+ if missing :
449+ msg = f"No configuration found for: { ', ' .join (missing )} ."
450+ raise ValueError (msg )
451+ return {d : all_distros [d ] for d in selected_distributions }
452+
453+ return all_distros
454+
455+
413456def excel_report_list (request : HttpRequest ) -> HttpResponse :
457+ context : dict [str , Any ] = {"distributions" : DISTRIBUTION_DESCRIPTIONS }
414458 reports : list [dict [str , str | float ]] = []
415459 if not EXCEL_REPORTS_DIR .exists ():
416- return render (request , "tests_interface/excel_reports.html" , {"reports" : reports })
460+ context ["reports" ] = reports
461+ return render (request , "tests_interface/excel_reports.html" , context )
417462
418463 for file_path in sorted (EXCEL_REPORTS_DIR .glob ("*.xlsx" ), reverse = True ):
419464 stat = file_path .stat ()
@@ -425,7 +470,8 @@ def excel_report_list(request: HttpRequest) -> HttpResponse:
425470 },
426471 )
427472
428- return render (request , "tests_interface/excel_reports.html" , {"reports" : reports })
473+ context ["reports" ] = reports
474+ return render (request , "tests_interface/excel_reports.html" , context )
429475
430476
431477def download_excel_report (request : HttpRequest , filename : str ) -> FileResponse : # noqa: ARG001
0 commit comments