Implemented a documentation compiler, initially for Aircraft
Forum thread: http://forum.flightgear.org/viewtopic.php?f=72&t=25119 The documentation compiler will iterate over all Aircraft and generate LaTeX files for each aircraft. It will then compile them to a pdf. Please refer to the forum thread for an example of such a pdf file. It will also generate a separate pdf file just for the checklists. Currently, the compiler uses the checklist files and additional files in a Docs/ subdirectory of the aircraft. To use this script, you need python > 2.7 and a recent version of pdflatex with hyperref support installed.
This commit is contained in:
parent
a401041637
commit
ad9525022e
1 changed files with 226 additions and 0 deletions
226
Docs/compile_docs.py
Executable file
226
Docs/compile_docs.py
Executable file
|
@ -0,0 +1,226 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import xml.etree.ElementTree as etree
|
||||
import os
|
||||
import shutil
|
||||
import re
|
||||
from subprocess import call
|
||||
|
||||
FG_ROOT="../"
|
||||
|
||||
def tex_escape(text):
|
||||
"""
|
||||
:param text: a plain text message
|
||||
:return: the message escaped to appear correctly in LaTeX
|
||||
"""
|
||||
conv = {
|
||||
'&': r'\&',
|
||||
'%': r'\%',
|
||||
'$': r'\$',
|
||||
'#': r'\#',
|
||||
'_': r'\_',
|
||||
'{': r'\{',
|
||||
'}': r'\}',
|
||||
'~': r'\textasciitilde{}',
|
||||
'^': r'\^{}',
|
||||
'\\': r'\textbackslash{}',
|
||||
'<': r'\textless',
|
||||
'>': r'\textgreater',
|
||||
}
|
||||
regex = re.compile('|'.join(re.escape(unicode(key)) for key in sorted(conv.keys(), key = lambda item: - len(item))))
|
||||
return regex.sub(lambda match: conv[match.group()], text)
|
||||
|
||||
def generate_airplane_latex(source):
|
||||
"""
|
||||
Generates the LaTeX files and directory structure for the procedures
|
||||
"""
|
||||
root = os.path.join("procedures", source['name'])
|
||||
checklists_path = os.path.join(root, "checklists")
|
||||
if(not os.path.isdir(root)):
|
||||
os.mkdir(root)
|
||||
if(not os.path.isdir(checklists_path)):
|
||||
os.mkdir(checklists_path)
|
||||
|
||||
# Write some warning notes
|
||||
preamble = open(os.path.join(root, "preamble.tex"), "w")
|
||||
|
||||
preamble.write("\\section{Preamble}")
|
||||
preamble.write("This procedure list should be considered incomplete and it is not intended for real world use. It is automatically generated documentation intended for FlightGear flight simulator.")
|
||||
|
||||
documentation_tex = open(os.path.join(root, source['dir'] + "_documentation.tex"), "w")
|
||||
documentation_tex.write("\\documentclass{article}\n")
|
||||
documentation_tex.write("\\usepackage{hyperref}\n\usepackage{graphicx}\n"
|
||||
"\\usepackage{fancyhdr}\\pagestyle{fancy}\n\\lfoot{Intended for FlightGear}\\rfoot{Not for real world use!}")
|
||||
documentation_tex.write("\\title{"+source['name']+" documentation}\n")
|
||||
documentation_tex.write("\\author{FlightGear team}\n")
|
||||
documentation_tex.write("\\renewcommand{\\familydefault}{\\sfdefault}")
|
||||
documentation_tex.write("\\begin{document}\n")
|
||||
documentation_tex.write("\\maketitle\n")
|
||||
|
||||
checklist_tex = open(os.path.join(root, source['dir'] + "_checklist.tex"), "w")
|
||||
checklist_tex.write("\\documentclass{article}\n")
|
||||
checklist_tex.write("\\usepackage{fancyhdr}\\pagestyle{fancy}\n\\lfoot{Intended for FlightGear}\\rfoot{Not for real world use!}")
|
||||
checklist_tex.write("\\begin{document}\n")
|
||||
|
||||
# If there's a thumbnail available, copy it into the documentation and include it
|
||||
# just before the beginning of the document, just after the title.
|
||||
if(source['thumbnail']):
|
||||
shutil.copyfile(FG_ROOT + "Aircraft/" + source['dir'] + "/thumbnail.jpg",
|
||||
os.path.join(root, "thumbnail.jpg"))
|
||||
documentation_tex.write("\\begin{figure}[h!]\\centering")
|
||||
documentation_tex.write("\\includegraphics[width=5cm,height=5cm,keepaspectratio]{thumbnail.jpg}")
|
||||
documentation_tex.write("\\end{figure}")
|
||||
documentation_tex.write("\\tableofcontents\n")
|
||||
documentation_tex.write("\\input{preamble.tex}\n")
|
||||
|
||||
# If any additional documentation, copy it to the procedures directory and
|
||||
# add it to the procedures.tex file
|
||||
if source['extra_documentation']:
|
||||
extra_docs_dir = os.path.join(FG_ROOT + "Aircraft/" + source['dir'], "Docs")
|
||||
extra_docs_dest_dir = os.path.join("procedures/" + source['name'], "Docs")
|
||||
if os.path.isdir(extra_docs_dest_dir):
|
||||
shutil.rmtree(extra_docs_dest_dir)
|
||||
shutil.copytree(extra_docs_dir, extra_docs_dest_dir)
|
||||
documentation_tex.write("\\input{Docs/" + source['dir'] + "_documentation}\n")
|
||||
|
||||
# If there are any checklist files parsed, write a title
|
||||
if(source['sources'] > 0):
|
||||
documentation_tex.write("\\input{checklists.tex}\n")
|
||||
checklists_tex = open(os.path.join(root, "checklists.tex"), "w")
|
||||
checklists_tex.write("\\section{Checklists}\n")
|
||||
|
||||
# and the checklists themselves.
|
||||
for xmlfile in source['sources']:
|
||||
tree = etree.parse(xmlfile)
|
||||
chkl_root = tree.getroot()
|
||||
for checklist in chkl_root:
|
||||
if checklist.tag != "checklist":
|
||||
print "Unrecognised tag in", xmlfile
|
||||
continue
|
||||
title = checklist.find("title")
|
||||
if title is None:
|
||||
title = "Untitled"
|
||||
else:
|
||||
title = tex_escape(title.text)
|
||||
items = []
|
||||
|
||||
for item in checklist.findall("item"):
|
||||
name = item.find("name")
|
||||
value = item.find("value")
|
||||
if name is None or value is None:
|
||||
continue
|
||||
if name.text is None or value.text is None:
|
||||
continue
|
||||
items.append({
|
||||
'name': tex_escape(name.text),
|
||||
'value': tex_escape(value.text)
|
||||
})
|
||||
filename = title + ".tex"
|
||||
filename = filename.replace("/", "_")
|
||||
filename = filename.replace(" ", "_")
|
||||
checklists_tex.write("\\subsection{" + title + "}\n")
|
||||
checklist_tex.write("\\section*{" + title + "}\n")
|
||||
checklists_tex.write("\\input{checklists/"+filename+"}\n")
|
||||
checklist_tex.write("\\input{checklists/"+filename+"}\n")
|
||||
f = open(os.path.join(checklists_path,filename), "w")
|
||||
if len(items) > 0:
|
||||
f.write("\\begin{description}\n")
|
||||
for item in items:
|
||||
f.write("\\item["+item['name']+"] \dotfill " + item['value']+"\n")
|
||||
f.write("\\end{description}\n")
|
||||
checklist_tex.write("\\clearpage\n")
|
||||
|
||||
documentation_tex.write("\\end{document}\n")
|
||||
checklist_tex.write("\\end{document}\n")
|
||||
|
||||
def gather_aircraft_metadata(directory = FG_ROOT + "Aircraft"):
|
||||
aircrafts = []
|
||||
for aircraft_directory in os.listdir(directory):
|
||||
for rootfile in os.listdir(os.path.join(directory, aircraft_directory)):
|
||||
rootfile = os.path.join(os.path.join(directory, aircraft_directory), rootfile)
|
||||
if not (os.path.isfile(rootfile) and rootfile.endswith("-set.xml")):
|
||||
continue
|
||||
# Houston, we found a set.xml. Let's read it. This implies an aircraft
|
||||
tree = etree.parse(rootfile)
|
||||
name = aircraft_directory
|
||||
|
||||
|
||||
for descr in tree.iter('description'):
|
||||
name = descr.text
|
||||
break
|
||||
aircraft = {
|
||||
'sources' : [],
|
||||
'name' : name,
|
||||
'dir' : aircraft_directory,
|
||||
'thumbnail': False,
|
||||
'extra_documentation': False
|
||||
}
|
||||
# Check if the aircraft provides additional documentation
|
||||
aircraft['extra_documentation'] = os.path.isdir(os.path.join(directory,
|
||||
aircraft_directory
|
||||
+ "/Docs/")) & os.path.isfile(os.path.join(directory, aircraft_directory
|
||||
+ "/Docs/"
|
||||
+ aircraft_directory
|
||||
+ "_documentation.tex"))
|
||||
# Check if there is a thumbnail
|
||||
if os.path.isfile(os.path.join(directory, aircraft_directory + "/thumbnail.jpg")):
|
||||
aircraft['thumbnail'] = True
|
||||
|
||||
for checklist in tree.iter('checklists'):
|
||||
aircraft['sources'].append(os.path.join(directory, aircraft_directory, checklist.attrib['include']))
|
||||
if 'include' in tree.getroot().attrib:
|
||||
try:
|
||||
tree = etree.parse(os.path.join(directory, aircraft_directory) + "/" + tree.getroot().attrib['include'])
|
||||
for checklist in tree.iter('checklists'):
|
||||
if 'include' in checklist.attrib:
|
||||
aircraft['sources'].append(os.path.join(directory, aircraft_directory, checklist.attrib['include']))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
if(len(aircraft['sources']) > 0 or aircraft['extra_documentation']):
|
||||
aircrafts.append(aircraft)
|
||||
|
||||
# Check if there are checklists that can be parsed
|
||||
# for root, dirs, files in os.walk(os.path.join(directory, aircraft_directory), topdown=False):
|
||||
# for name in files:
|
||||
# if(name.endswith("checklists.xml")):
|
||||
# aircraft['sources'].append(os.path.join(root,name))
|
||||
|
||||
return aircrafts
|
||||
|
||||
|
||||
def compile_airplane_latex(source):
|
||||
"""
|
||||
Compile the procedures using pdflatex
|
||||
"""
|
||||
wd = os.getcwd()
|
||||
root = os.path.join("procedures", source['name'])
|
||||
os.chdir(root)
|
||||
with open(os.devnull, "w") as fnull:
|
||||
mainfile = source['dir'] + "_documentation.tex"
|
||||
call(["pdflatex", "-interaction", "nonstopmode", mainfile], stdout=fnull,stderr=fnull)
|
||||
call(["pdflatex", "-interaction", "nonstopmode", mainfile], stdout=fnull,stderr=fnull)
|
||||
mainfile = source['dir'] + "_checklist.tex"
|
||||
call(["pdflatex", "-interaction", "nonstopmode", mainfile], stdout=fnull,stderr=fnull)
|
||||
call(["pdflatex", "-interaction", "nonstopmode", mainfile], stdout=fnull,stderr=fnull)
|
||||
os.chdir(wd)
|
||||
|
||||
|
||||
def generate_airplane_documentation():
|
||||
# First generate the index of what should be included in the
|
||||
# documentation
|
||||
aircrafts = gather_aircraft_metadata("../Aircraft")
|
||||
if(len(aircrafts) == 0 ):
|
||||
print "No aircraft found; wrong directory?"
|
||||
if(not os.path.isdir("procedures")):
|
||||
os.mkdir("procedures")
|
||||
|
||||
# Then generate the documentation per airplane
|
||||
for aircraft in aircrafts:
|
||||
generate_airplane_latex(aircraft)
|
||||
compile_airplane_latex(aircraft)
|
||||
|
||||
# The main procedure
|
||||
generate_airplane_documentation()
|
Loading…
Reference in a new issue