docs.py 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. #!/usr/bin/env python3
  2. import argparse
  3. from collections import defaultdict
  4. import jinja2
  5. import os
  6. from enum import Enum
  7. from natsort import natsorted
  8. from typing import Dict, List
  9. from cereal import car
  10. from common.basedir import BASEDIR
  11. from selfdrive.car import gen_empty_fingerprint
  12. from selfdrive.car.docs_definitions import CarInfo, Column, CommonFootnote
  13. from selfdrive.car.car_helpers import interfaces, get_interface_attr
  14. def get_all_footnotes() -> Dict[Enum, int]:
  15. all_footnotes = list(CommonFootnote)
  16. for footnotes in get_interface_attr("Footnote", ignore_none=True).values():
  17. all_footnotes.extend(footnotes)
  18. return {fn: idx + 1 for idx, fn in enumerate(all_footnotes)}
  19. CARS_MD_OUT = os.path.join(BASEDIR, "docs", "CARS.md")
  20. CARS_MD_TEMPLATE = os.path.join(BASEDIR, "selfdrive", "car", "CARS_template.md")
  21. def get_all_car_info() -> List[CarInfo]:
  22. all_car_info: List[CarInfo] = []
  23. footnotes = get_all_footnotes()
  24. for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items():
  25. CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), car_fw=[car.CarParams.CarFw(ecu="unknown")])
  26. if CP.dashcamOnly or car_info is None:
  27. continue
  28. # A platform can include multiple car models
  29. if not isinstance(car_info, list):
  30. car_info = (car_info,)
  31. for _car_info in car_info:
  32. if not hasattr(_car_info, "row"):
  33. _car_info.init_make(CP)
  34. _car_info.init(CP, footnotes)
  35. all_car_info.append(_car_info)
  36. # Sort cars by make and model + year
  37. sorted_cars: List[CarInfo] = natsorted(all_car_info, key=lambda car: car.name.lower())
  38. return sorted_cars
  39. def group_by_make(all_car_info: List[CarInfo]) -> Dict[str, List[CarInfo]]:
  40. sorted_car_info = defaultdict(list)
  41. for car_info in all_car_info:
  42. sorted_car_info[car_info.make].append(car_info)
  43. return dict(sorted_car_info)
  44. def generate_cars_md(all_car_info: List[CarInfo], template_fn: str) -> str:
  45. with open(template_fn, "r") as f:
  46. template = jinja2.Template(f.read(), trim_blocks=True, lstrip_blocks=True)
  47. footnotes = [fn.value.text for fn in get_all_footnotes()]
  48. cars_md: str = template.render(all_car_info=all_car_info, group_by_make=group_by_make,
  49. footnotes=footnotes, Column=Column)
  50. return cars_md
  51. if __name__ == "__main__":
  52. parser = argparse.ArgumentParser(description="Auto generates supported cars documentation",
  53. formatter_class=argparse.ArgumentDefaultsHelpFormatter)
  54. parser.add_argument("--template", default=CARS_MD_TEMPLATE, help="Override default template filename")
  55. parser.add_argument("--out", default=CARS_MD_OUT, help="Override default generated filename")
  56. args = parser.parse_args()
  57. with open(args.out, 'w') as f:
  58. f.write(generate_cars_md(get_all_car_info(), args.template))
  59. print(f"Generated and written to {args.out}")