1
0
Fork 0
fgdata/Docs/compile_docs.py
Ruben De Smet ad9525022e 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.
2015-01-14 18:42:04 -05:00

226 lines
9.6 KiB
Python
Executable file

#!/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()