#!/usr/bin/env python3
import argparse
from collections import defaultdict
import jinja2
import os
from enum import Enum
from natsort import natsorted
from typing import Dict , List
from cereal import car
from common . basedir import BASEDIR
from selfdrive . car import gen_empty_fingerprint
from selfdrive . car . docs_definitions import CarInfo , Column , CommonFootnote
from selfdrive . car . car_helpers import interfaces , get_interface_attr
def get_all_footnotes ( ) - > Dict [ Enum , int ] :
all_footnotes = list ( CommonFootnote )
for footnotes in get_interface_attr ( " Footnote " , ignore_none = True ) . values ( ) :
all_footnotes . extend ( footnotes )
return { fn : idx + 1 for idx , fn in enumerate ( all_footnotes ) }
CARS_MD_OUT = os . path . join ( BASEDIR , " docs " , " CARS.md " )
CARS_MD_TEMPLATE = os . path . join ( BASEDIR , " selfdrive " , " car " , " CARS_template.md " )
def get_all_car_info ( ) - > List [ CarInfo ] :
all_car_info : List [ CarInfo ] = [ ]
footnotes = get_all_footnotes ( )
for model , car_info in get_interface_attr ( " CAR_INFO " , combine_brands = True ) . items ( ) :
CP = interfaces [ model ] [ 0 ] . get_params ( model , fingerprint = gen_empty_fingerprint ( ) , car_fw = [ car . CarParams . CarFw ( ecu = " unknown " ) ] )
if CP . dashcamOnly or car_info is None :
continue
# A platform can include multiple car models
if not isinstance ( car_info , list ) :
car_info = ( car_info , )
for _car_info in car_info :
if not hasattr ( _car_info , " row " ) :
_car_info . init ( CP , footnotes )
all_car_info . append ( _car_info )
# Sort cars by make and model + year
sorted_cars : List [ CarInfo ] = natsorted ( all_car_info , key = lambda car : car . name . lower ( ) )
return sorted_cars
def group_by_make ( all_car_info : List [ CarInfo ] ) - > Dict [ str , List [ CarInfo ] ] :
sorted_car_info = defaultdict ( list )
for car_info in all_car_info :
sorted_car_info [ car_info . make ] . append ( car_info )
return dict ( sorted_car_info )
def generate_cars_md ( all_car_info : List [ CarInfo ] , template_fn : str ) - > str :
with open ( template_fn , " r " ) as f :
template = jinja2 . Template ( f . read ( ) , trim_blocks = True , lstrip_blocks = True )
footnotes = [ fn . value . text for fn in get_all_footnotes ( ) ]
cars_md : str = template . render ( all_car_info = all_car_info , group_by_make = group_by_make ,
footnotes = footnotes , Column = Column )
return cars_md
if __name__ == " __main__ " :
parser = argparse . ArgumentParser ( description = " Auto generates supported cars documentation " ,
formatter_class = argparse . ArgumentDefaultsHelpFormatter )
parser . add_argument ( " --template " , default = CARS_MD_TEMPLATE , help = " Override default template filename " )
parser . add_argument ( " --out " , default = CARS_MD_OUT , help = " Override default generated filename " )
args = parser . parse_args ( )
with open ( args . out , ' w ' ) as f :
f . write ( generate_cars_md ( get_all_car_info ( ) , args . template ) )
print ( f " Generated and written to { args . out } " )