#!/usr/bin/env python3
import argparse
from collections import defaultdict
import difflib
import pickle
from selfdrive . car . docs import get_all_car_info
from selfdrive . car . docs_definitions import Column
FOOTNOTE_TAG = " <sup> {} </sup> "
STAR_ICON = ' <a href= " ## " ><img valign= " top " src= " https://raw.githubusercontent.com/commaai/openpilot/master/docs/assets/icon-star- {} .svg " width= " 22 " /></a> '
VIDEO_ICON = ' <a href= " {} " target= " _blank " ><img height= " 18px " src= " https://raw.githubusercontent.com/commaai/openpilot/master/docs/assets/icon-youtube.svg " ></img></a> '
COLUMNS = " | " + " | " . join ( [ column . value for column in Column ] ) + " | "
COLUMN_HEADER = " |---|---|---| {} | " . format ( " | " . join ( [ " :---: " ] * ( len ( Column ) - 3 ) ) )
ARROW_SYMBOL = " ➡️ "
def load_base_car_info ( path ) :
with open ( path , " rb " ) as f :
return pickle . load ( f )
def match_cars ( base_cars , new_cars ) :
changes = [ ]
additions = [ ]
for new in new_cars :
# Addition if no close matches or close match already used
# Change if close match and not already used
matches = difflib . get_close_matches ( new . name , [ b . name for b in base_cars ] , cutoff = 0. )
if not len ( matches ) or matches [ 0 ] in [ c [ 1 ] . name for c in changes ] :
additions . append ( new )
else :
changes . append ( ( new , next ( car for car in base_cars if car . name == matches [ 0 ] ) ) )
# Removal if base car not in changes
removals = [ b for b in base_cars if b . name not in [ c [ 1 ] . name for c in changes ] ]
return changes , additions , removals
def build_column_diff ( base_car , new_car ) :
row_builder = [ ]
for column in Column :
base_column = base_car . get_column ( column , STAR_ICON , VIDEO_ICON , FOOTNOTE_TAG )
new_column = new_car . get_column ( column , STAR_ICON , VIDEO_ICON , FOOTNOTE_TAG )
if base_column != new_column :
row_builder . append ( f " { base_column } { ARROW_SYMBOL } { new_column } " )
else :
row_builder . append ( new_column )
return format_row ( row_builder )
def format_row ( builder ) :
return " | " + " | " . join ( builder ) + " | "
def print_car_info_diff ( path ) :
base_car_info = defaultdict ( list )
new_car_info = defaultdict ( list )
for car in load_base_car_info ( path ) :
base_car_info [ car . car_fingerprint ] . append ( car )
for car in get_all_car_info ( ) :
new_car_info [ car . car_fingerprint ] . append ( car )
# Add new platforms to base cars so we can detect additions and removals in one pass
base_car_info . update ( { car : [ ] for car in new_car_info if car not in base_car_info } )
changes = defaultdict ( list )
for base_car_model , base_cars in base_car_info . items ( ) :
# Match car info changes, and get additions and removals
new_cars = new_car_info [ base_car_model ]
car_changes , car_additions , car_removals = match_cars ( base_cars , new_cars )
# Removals
for car_info in car_removals :
changes [ " removals " ] . append ( format_row ( [ car_info . get_column ( column , STAR_ICON , VIDEO_ICON , FOOTNOTE_TAG ) for column in Column ] ) )
# Additions
for car_info in car_additions :
changes [ " additions " ] . append ( format_row ( [ car_info . get_column ( column , STAR_ICON , VIDEO_ICON , FOOTNOTE_TAG ) for column in Column ] ) )
for new_car , base_car in car_changes :
# Column changes
row_diff = build_column_diff ( base_car , new_car )
if ARROW_SYMBOL in row_diff :
changes [ " column " ] . append ( row_diff )
# Detail sentence changes
if base_car . detail_sentence != new_car . detail_sentence :
changes [ " detail " ] . append ( f " - Sentence for { base_car . name } changed! \n " +
" ```diff \n " +
f " - { base_car . detail_sentence } \n " +
f " + { new_car . detail_sentence } \n " +
" ``` " )
# Print diff
if any ( len ( c ) for c in changes . values ( ) ) :
markdown_builder = [ " ### ⚠️ This PR makes changes to [CARS.md](../blob/master/docs/CARS.md) ⚠️ " ]
for title , category in ( ( " ## 🔀 Column Changes " , " column " ) , ( " ## ❌ Removed " , " removals " ) , ( " ## ➕ Added " , " additions " ) , ( " ## 📖 Detail Sentence Changes " , " detail " ) ) :
if len ( changes [ category ] ) :
markdown_builder . append ( title )
if category not in ( " detail " , ) :
markdown_builder . append ( COLUMNS )
markdown_builder . append ( COLUMN_HEADER )
markdown_builder . extend ( changes [ category ] )
print ( " \n " . join ( markdown_builder ) )
if __name__ == " __main__ " :
parser = argparse . ArgumentParser ( )
parser . add_argument ( " --path " , required = True )
args = parser . parse_args ( )
print_car_info_diff ( args . path )