#!/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 = "{}" STAR_ICON = '' 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, FOOTNOTE_TAG) new_column = new_car.get_column(column, STAR_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, 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, 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)