From 8c145b7e49a8ecd2e29622d5725bde21431ecf2a Mon Sep 17 00:00:00 2001 From: Matteo Barbera Date: Mon, 24 Jun 2019 21:56:14 +0200 Subject: [PATCH] Improvement to start.py maintenance tools (#2438) * Added last commit dates to lists of untested flight plans and airframes * Improved paparazzi_health to include a list of all modules and their usage * improved paparazzi_health to include a list of board makefiles not used by any airframe * improved paparazzi_health to generate list of unused airframes, flightplans and boards sorted by most recently changed * airframe details now stores modules as tuple of name and type: [(name, type), ...] * added seeing includes in paparazzi_health * Removed unneccesary print statements * Added widget to start.py to generate html table of the module usage of the airframes for the selected conf * improved html table readability and sorted airframe names alphabetically * fixed alphabetical ordering of airframe name being case sensitive and added comments * Added module class, more info about modules now gets printed. Commit dates should still be fixed * Did speed improvement, but still slow * split up more info and lists of untested files * Added checkboxes to start.py to give user option to show airframe detail, or untested files, or both * Cleaned up some code * Added separator to make link between checkboxes and MoreInfo button clearer * Fixed code style * Added description to the module listing * one should now be able to select which aspects to show in the maintenance tools * Initial version of maintenance tools window * Now also checks if modules are mentioned in the settings modules in an userconf * Improved layout of maintenance tool window * Improved variable names and tooltip information * Small QoL changes to prepare for pull request * Fixed a bug with the untested boards not showing correctly if airframes was not also selected and fixed some Codacy issues * Added functionality to generate module overview by board name * Module overview table now also displays the xml file and of the airframe * Update health_monitor_update branch with master changes * Bug-fixes and added description to maintenance tools --- start.py | 49 +++++++++++++++++++++++---- sw/lib/python/paparazzi_health.py | 55 ++++++++++++++++++++++++++----- 2 files changed, 89 insertions(+), 15 deletions(-) diff --git a/start.py b/start.py index 0c6c1a87ea..1d7038635c 100755 --- a/start.py +++ b/start.py @@ -21,6 +21,7 @@ from paparazzi_health import PaparazziOverview import xml.etree.ElementTree + class ConfChooser(object): # General Functions @@ -29,10 +30,11 @@ class ConfChooser(object): combo.set_sensitive(False) combo.get_model().clear() current_index = 0 + combo.append_text("------None Selected------") for (i, text) in enumerate(clist): combo.append_text(text) - if os.path.join(paparazzi.conf_dir, text) == os.path.realpath(active): - current_index = i + if active is not None and os.path.join(paparazzi.conf_dir, text) == os.path.realpath(active): + current_index = i + 1 # Add one due to ---None Selected--- combo.set_active(current_index) combo.set_sensitive(True) @@ -70,6 +72,10 @@ class ConfChooser(object): conf_files = paparazzi.get_list_of_conf_files(self.exclude_backups) self.update_combo(combo, conf_files, self.conf_xml) + def find_board_files(self, combo): + board_files = paparazzi.get_list_of_boards() + self.update_combo(combo, board_files, None) + def find_controlpanel_files(self): controlpanel_files = paparazzi.get_list_of_controlpanel_files(self.exclude_backups) self.update_combo(self.controlpanel_file_combo, controlpanel_files, self.controlpanel_xml) @@ -135,8 +141,10 @@ class ConfChooser(object): show_modules=data["Modules"].get_active()) def module_usage(self, widget, data): - selected_conf = data.get_active_text() - self.obj.airframe_module_overview(selected_conf) + if data["Conf"].get_active() is not 0: + self.obj.airframe_module_overview(data["Conf"].get_active_text()) + elif data["Board"].get_active() is not 0: + self.obj.airframe_module_overview(data["Board"].get_active_text() + ".makefile") def launch(self, widget): self.accept(widget) @@ -263,20 +271,39 @@ class ConfChooser(object): def changed_cb(self, widget, data): self.count_airframes_in_conf(data["combo"], data["list"]) + @staticmethod + def deactivate_cb(widget, combo): + current_selection = widget.get_active() + combo.set_active(0) + widget.set_active(current_selection) + def maintenance_window(self, widget): mtn_window = gtk.Window() mtn_window.set_position(gtk.WIN_POS_CENTER) - mtn_window.set_size_request(750, 300) + mtn_window.set_size_request(750, 360) mtn_window.set_title("Maintenance Tools") mnt_vbox = gtk.VBox() + mnt_desc_label = gtk.Label("") + desc_text = "Show module usage of all airframes in a selected conf file or all airframes " \ + "with a specific board across all conf files." + mnt_desc_label.set_markup(desc_text) + mnt_desc_label.set_size_request(720, 40) + mnt_desc_label.set_line_wrap(True) + mnt_conf_label = gtk.Label("Conf:") mnt_conf_label.set_size_request(100, 30) mnt_conf_file_combo = gtk.combo_box_new_text() self.find_conf_files(mnt_conf_file_combo) mnt_conf_file_combo.set_size_request(500, 30) + mnt_board_label = gtk.Label("Board:") + mnt_board_label.set_size_request(100, 30) + mnt_board_file_combo = gtk.combo_box_new_text() + self.find_board_files(mnt_board_file_combo) + mnt_board_file_combo.set_size_request(500, 30) + mnt_conf_airframes = gtk.Label("") self.count_airframes_in_conf(mnt_conf_file_combo, mnt_conf_airframes) mnt_conf_airframes.set_size_request(650, 180) @@ -284,14 +311,24 @@ class ConfChooser(object): combo_list = {"combo": mnt_conf_file_combo, "list": mnt_conf_airframes} mnt_conf_file_combo.connect("changed", self.changed_cb, combo_list) + mnt_conf_file_combo.connect("changed", self.deactivate_cb, mnt_board_file_combo) + + mnt_board_file_combo.connect("changed", self.deactivate_cb, mnt_conf_file_combo) + + mnt_combos = {"Conf": mnt_conf_file_combo, "Board": mnt_board_file_combo} mnt_confbar = gtk.HBox() mnt_confbar.pack_start(mnt_conf_label) mnt_confbar.pack_start(mnt_conf_file_combo) + mnt_boardbar = gtk.HBox() + mnt_boardbar.pack_start(mnt_board_label) + mnt_boardbar.pack_start(mnt_board_file_combo) + mnt_vbox.pack_start(mnt_desc_label) mnt_vbox.pack_start(mnt_confbar, False) + mnt_vbox.pack_start(mnt_boardbar, False) btnModule = gtk.Button("Module\nUsage") - btnModule.connect("clicked", self.module_usage, mnt_conf_file_combo) + btnModule.connect("clicked", self.module_usage, mnt_combos) btnModule.set_tooltip_text("More information on the modules used by these airframes") mnt_caexbar = gtk.HBox() diff --git a/sw/lib/python/paparazzi_health.py b/sw/lib/python/paparazzi_health.py index 2fe049fe83..b21c4d6a07 100755 --- a/sw/lib/python/paparazzi_health.py +++ b/sw/lib/python/paparazzi_health.py @@ -8,6 +8,7 @@ import datetime from fnmatch import fnmatch import re import numpy as np +from functools import wraps from collections import Counter import paparazzi import xml.etree.ElementTree @@ -17,6 +18,34 @@ import subprocess PIPE = subprocess.PIPE +def conf_board_decorator(module_overview_func): + """ + This decorator extends the function PaparazziOverview.airframe_module_overview to generate the html + table either for airframes in a conf file or all airframes with a specific board + """ + @wraps(module_overview_func) + def wrapper(self, selected_file): + + if selected_file[-3:] == "xml": + airframes = self.list_airframes_in_conf(selected_file) + module_overview_func(self, iter(airframes)) + elif selected_file[-8:] == "makefile": + board = selected_file[:-9] + conf_files = iter(paparazzi.get_list_of_conf_files()) + airframes = [] + for conf in conf_files: + afs_in_conf = iter(self.list_airframes_in_conf(conf)) + for af in afs_in_conf: + ac = self.airframe_details(af.xml) + if board in ac.boards: + airframes.append(af) + module_overview_func(self, iter(airframes)) + else: + raise ValueError("selected file is neither a conf.xml or board.makefile") + + return wrapper + + class Airframe: name = "" ac_id = "" @@ -318,19 +347,25 @@ class PaparazziOverview(object): def generate_sorted_list(self, lst): commit_dates = [re.sub(r"( \n)$", "", self.get_last_commit_date(paparazzi.conf_dir + elm)) for elm in lst] - file_date_lst = sorted(zip(lst, commit_dates), key=lambda x: datetime.datetime.strptime(x[1], '%d-%m-%Y'), + untracked_zip = [] + tracked_zip = [] + for z in zip(lst, commit_dates): + if z[1] != "00-00-0000": + tracked_zip.append(z) + else: + untracked_zip.append((z[0], "Untracked file")) + file_date_lst = untracked_zip + sorted(tracked_zip, key=lambda x: datetime.datetime.strptime(x[1], '%d-%m-%Y'), reverse=True) return file_date_lst - def airframe_module_overview(self, selected_conf): + @conf_board_decorator + def airframe_module_overview(self, airframes): """ Creates a nested dictionary of the airframe names and the modules they use to generate an html table that provides an overview of module usage of the airframes of interest - :param selected_conf: str of the name of conf file + :param airframes: list or iterator of airframes to be analyzed """ - # Looks at airframes in selected conf (not the currently active conf) - airframes = self.list_airframes_in_conf(selected_conf) # Structure of nested dictionary: {af name: {module name: module type}} afs_mods = {} @@ -350,14 +385,16 @@ class PaparazziOverview(object): del unique_mods_ctr['xml'] # Table initialization, airframe names are ordered alphabetically, module names by most used - ac_mod_table = np.zeros((len(afs_mods.keys()) + 1, len(unique_mods_ctr.keys()) + 1), dtype=object) + ac_mod_table = np.zeros((len(afs_mods.keys()) + 1, len(unique_mods_ctr.keys()) + 2), dtype=object) ac_mod_table[0, 0] = "Name \\ Modules" + ac_mod_table[0, 1] = "XML File" ac_mod_table[1:, 0] = sorted(afs_mods.keys(), key=lambda s: s.lower()) - ac_mod_table[0, 1:] = [i for i, _ in unique_mods_ctr.most_common()] + ac_mod_table[0, 2:] = [i for i, _ in unique_mods_ctr.most_common()] for i in range(1, len(ac_mod_table[:, 0])): - for j in range(1, len(ac_mod_table[0, :])): - ac_name = ac_mod_table[i, 0] + ac_name = ac_mod_table[i, 0] + ac_mod_table[i, 1] = afs_mods[ac_name]["xml"][0] + for j in range(2, len(ac_mod_table[0, :])): module_name = ac_mod_table[0, j] if ac_mod_table[0, j] not in afs_mods[ac_name]: ac_mod_table[i, j] = "\\"