Skip to content
Snippets Groups Projects
Commit 6f7b33c9 authored by Sebastian Oberschwendtner's avatar Sebastian Oberschwendtner
Browse files

Adds first version for documenting the AcXML.

parent 77b04a4e
No related branches found
No related tags found
3 merge requests!76Draft: Updated Python code example,!73Initial open source version,!1Doxygen setup for contionuous documentation
Pipeline #1075295 waiting for manual action
# Ignore generated Doxygen docs in modules folder
/docs/modules/**/
docs/modules/**/
# Ignore the local docs of the AcXML
docs/aircraft-xml/
......@@ -39,4 +39,28 @@ date: yyyy-mm-dd
# MyPage
<Content goes here>
```
# Tools
The documentation page uses some custom tools to enable a seamless continuous documentation:
## document_aircraft_xml.py
> Script
This script is used to document the aircraft exchange file.
It parses the XML file and creates the documentation for each node using its description.
The usage is explained in the output when calling the script with the `--help` option:
```sh
usage: document_aircraft_xml.py [-h] [--title title] [--level level] filename
Converts an aircraft XML file to a markdown file. The output is streamed to stdout and can be piped to a file.
positional arguments:
filename The XML file to convert.
options:
-h, --help show this help message and exit
--title title The title of the output page. This also sets the root node which is used to create the document.
--level level The maximum level nodes to be used as headers.
```
\ No newline at end of file
......@@ -104,6 +104,21 @@ nav:
- 'Style Guide':
- 'C++': 'developer/style/cpp.md'
- 'Python': 'developer/style/python.md'
- 'Aircraft Exchange File':
- 'General': 'aircraft-xml/general.md'
- 'MassesAndLoadings': 'aircraft-xml/masses.md'
- 'Geometry': 'aircraft-xml/geometry.md'
- 'Structure': 'aircraft-xml/structure.md'
- 'Accommodation': 'aircraft-xml/accommodation.md'
- 'Propulsion': 'aircraft-xml/propulsion.md'
- 'Systems': 'aircraft-xml/systems.md'
- 'Aerodynamics': 'aircraft-xml/aerodynamics.md'
- 'StabilityAndControlCharacteristics': 'aircraft-xml/stability.md'
- 'Performance': 'aircraft-xml/performance.md'
- 'MonetaryValues': 'aircraft-xml/monetary.md'
- 'EcologicalValues': 'aircraft-xml/ecological.md'
- 'Requirements': 'aircraft-xml/requirements.md'
- 'DesignSpecification': 'aircraft-xml/specification.md'
- 'About':
- 'license.md'
- 'contact.md'
......
Source diff could not be displayed: it is too large. Options to address this: view the blob.
#!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": "# {: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": "Desc",
"Unit": "Unit",
}
# === 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.
"""
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.
This function adds all subnodes of the given node to the page.
It recursively calls itself for all subnodes.
It does not add sections which are already present in the current level.
Args:
node (ET.Element): Node to start from.
"""
# Only add the header if it was not already added at this level
if node.tag in self.sections[self.current_level]:
return
# Check whether the current entry can be a header
if not self.max_level_reached:
self.sections[self.current_level].append(node.tag)
self.make_header_entry(node)
self.current_path = Path(node.tag)
else:
self.current_path /= node.tag
# Loop through the children and group single entries in a table
for child in node:
if len(child) == 0:
self.make_table_entry(child)
# Add a new level of sections
self.current_level += 1
self.sections.append([])
# Loop again and add the subnodes
for child in node:
if len(child) > 0:
self.create(child)
# Decrease the level again when finished
self.current_level -= 1
self.sections.pop()
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
# Create the header with description
print(
"\n"
+ self.current_level * "#"
+ FORMAT["Header"].format(
node.tag, node.attrib.get(KEYS["Description"], "None")
)
)
# Try to add a unit description
try:
print(FORMAT["Unit"].format(node.attrib[KEYS["Unit"]]))
except KeyError:
pass
def make_table_entry(self, node: ET.Element):
"""Creates a table entry.
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
print(FORMAT["TableHeader"])
# Since we use pathlib for convenience, we need to convert it to a string
# and replace the backslashes with forward slashes
path_name = str(self.current_path / node.tag).replace("\\", "/")
# Create the header with description
print(
FORMAT["Table"].format(
path_name,
node.attrib.get(KEYS["Unit"], "-"),
node.attrib.get(KEYS["Description"], "None"),
)
)
# === 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."
)
# Add the filename argument
parser.add_argument(
"filename", metavar="filename", type=str, help="The XML file to convert."
)
# Add the layout arguments
parser.add_argument(
"--title",
metavar="title",
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()
# Read the XML file
tree = ET.parse(args.filename)
root = tree.getroot()
# Get the configuration parameters
page = Page(args.title, args.level)
node = root.find(page.title)
# Check whether the node exists
if node is None:
raise Warning("Could not find node with title '" + page.title + "'")
# Start creating the page
page.create(node)
if __name__ == "__main__":
main()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment