From 69c7d261c9b9376f653774e440c4aea20ac67161 Mon Sep 17 00:00:00 2001 From: Kristina Mazur <kristina.mazur@tum.de> Date: Tue, 3 Dec 2024 14:54:58 +0100 Subject: [PATCH] Enhance it --- scripts/document_aircraft_xml.py | 157 +++++++------------------------ 1 file changed, 36 insertions(+), 121 deletions(-) diff --git a/scripts/document_aircraft_xml.py b/scripts/document_aircraft_xml.py index 453ee3c..06c53f9 100644 --- a/scripts/document_aircraft_xml.py +++ b/scripts/document_aircraft_xml.py @@ -1,104 +1,49 @@ -#!python -# Copyright (c) 2023 S. Oberschwendtner. -# -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <https://www.gnu.org/licenses/>. - -"""! @brief Script for converting an aircraft XML file to a content page.""" -## -# @file document_aircraft_xml.py -# -# @brief Script for converting an aircraft XML file to a content page for the UNICADO homepage. -# -# @section description_document_aircraft_xml Description -# This script parse the entries of an aircraft XML file and extracts -# the entries and documents them using their description tags. -# The output is a markdown file which can be used for the documentation. -# -# @section libraries_document_aircraft_xml Libraries/Modules -# - argparse standard library (https://docs.python.org/3/library/argparse.html) -# - Parser for command-line options, arguments and sub-commands. -# - xml standard library (https://docs.python.org/3/library/xml.html) -# - XML Processing Modules. -# - pathlib standard library (https://docs.python.org/3/library/pathlib.html) -# - Object-oriented filesystem paths. -# -# @section notes_document_aircraft_xml Notes -# - None. -# -# @section todo_document_aircraft_xml TODO -# - None. -# -# @section authors_document_aircraft_xml Author(s) -# - Created by S. Oberschwendtner on 19/09/2023. - -# === Imports === import argparse import xml.etree.ElementTree as ET from pathlib import Path # === Configuration === -# Define the format of the output FORMAT = { - "Header": "\n## {:s}\n> **Description**: {:s}\n", + "Header": "\n{}## {:s}\n> **Description**: {:s}\n", "Unit": "> **Unit**: {:s}\n", "TableHeader": "| Relative XML Path | Unit | Description |\n|:---|:---:|:---|", "Table": "| <nobr>`{:s}` | *{:s}* | {:s} |", } -# Which keys are used in the XML attributes for which information KEYS = { - "Description": "description", # Match the 'description' key from XML - "Unit": "unit", # Match the 'unit' key from XML + "Description": "description", + "Unit": "unit", } +# Define specific levels for each child node of "requirements_and_specifications" +CUSTOM_LEVELS = { + "general": 2, + "mission_files": 2, + "design_specification": 4, + "requirements": 3, +} - -# === Classes === class Page: - """Class for a page. - - A page is one top section of the aircraft XML file. - """ - - @property - def max_level_reached(self) -> bool: - """Returns whether the maximum level has been reached.""" - return self.current_level >= self.max_header_level - - def __init__(self, title, max_level): - """Constructor of the class. - - Args: - title (str): Title of the page. - max_level (int): Maximum level of the headers. - """ + def __init__(self, title): self.title = title - self.max_header_level = max_level self.current_level = 0 self.table_started = False self.current_path = Path(".") self.sections = [[]] - def create(self, node: ET.Element): - """Creates the page from the given node, summarizing nested elements properly. - - Args: - node (ET.Element): Node to start from. - """ - # Add header if appropriate - if not self.max_level_reached: + def create(self, node: ET.Element, level=None): + # Set the level based on custom levels if provided + if node.tag in CUSTOM_LEVELS: + level = CUSTOM_LEVELS[node.tag] + elif level is None: + level = self.current_level + 1 + + # Update current level and path + if level <= 4: + self.current_level = level + self.current_path /= node.tag + + # Add header if appropriate self.make_header_entry(node) # Treat node attributes and values as part of the parent row @@ -110,28 +55,25 @@ class Page: # Process child elements recursively, skipping common attributes like 'value', 'unit', etc. for child in node: - if child.tag in ["value", "unit", "lower_boundary", "upper_boundary"]: - continue - self.create(child) + if child.tag not in ["value", "unit", "lower_boundary", "upper_boundary"]: + self.create(child, level + 1) + # Restore previous path after processing + self.current_path = self.current_path.parent def make_header_entry(self, node: ET.Element): - """Creates a header entry. - - Args: - node (ET.Element): The current node element. - """ # Reset the table when creating a new header self.table_started = False - # Fetch description + # Fetch description and adjust header level format description = node.attrib.get(KEYS["Description"], "None") + header_level_prefix = "#" * min(self.current_level, 4) # Create the header with description print( "\n" - + self.current_level * "#" - + FORMAT["Header"].format(node.tag, description) + + header_level_prefix + + FORMAT["Header"].format(self.current_level, node.tag, description) ) # Try to add a unit description @@ -140,11 +82,6 @@ class Page: print(FORMAT["Unit"].format(unit)) def make_table_entry(self, node: ET.Element): - """Creates a table entry for the current node, embedding child attributes into the parent node's row. - - Args: - node (ET.Element): The current node element. - """ # Check if the table has already been started if not self.table_started: self.table_started = True @@ -153,29 +90,14 @@ class Page: # Generate a relative XML path for the current node path_name = str(self.current_path / node.tag).replace("\\", "/") - # Fetch parent description and unit + # Fetch the description and unit description = node.attrib.get(KEYS["Description"], f"No description for {node.tag}") unit = node.attrib.get(KEYS["Unit"], "No unit specified") - # Collect all child attributes - child_details = [] - for child in node: - child_desc = child.attrib.get(KEYS["Description"], f"No description for {child.tag}") - child_unit = child.attrib.get(KEYS["Unit"], "No unit specified") - child_details.append(f"{child.tag}: {child_desc} (unit: {child_unit})") - - # Combine parent description with child attributes - combined_description = description + " | " + " | ".join(child_details) - - # Print the table row for the parent node - print(FORMAT["Table"].format(path_name, unit, combined_description)) + # Print the table row for the current node + print(FORMAT["Table"].format(path_name, unit, description)) -# === Main === def main(): - """ - Main function of the script. - """ - # Create argument parser parser = argparse.ArgumentParser( description="Converts an aircraft XML file to a markdown file. The output is streamed to stdout and can be piped to a file." ) @@ -192,12 +114,6 @@ def main(): type=str, help="The title of the output page. This also sets the root node which is used to create the document.", ) - parser.add_argument( - "--level", - metavar="level", - type=int, - help="The maximum level nodes to be used as headers.", - ) # Parse the arguments args = parser.parse_args() @@ -207,7 +123,7 @@ def main(): root = tree.getroot() # Get the configuration parameters - page = Page(args.title, args.level) + page = Page(args.title) node = root.find(page.title) # Check whether the node exists @@ -217,6 +133,5 @@ def main(): # Start creating the page page.create(node) - if __name__ == "__main__": main() -- GitLab