1#!/usr/bin/python 2 3import xml.etree.ElementTree as etree 4import os 5import shutil 6import re 7from subprocess import call 8 9FG_ROOT="../" 10 11def tex_escape(text): 12 """ 13 :param text: a plain text message 14 :return: the message escaped to appear correctly in LaTeX 15 """ 16 conv = { 17 '&': r'\&', 18 '%': r'\%', 19 '$': r'\$', 20 '#': r'\#', 21 '_': r'\_', 22 '{': r'\{', 23 '}': r'\}', 24 '~': r'\textasciitilde{}', 25 '^': r'\^{}', 26 '\\': r'\textbackslash{}', 27 '<': r'\textless', 28 '>': r'\textgreater', 29 } 30 regex = re.compile('|'.join(re.escape(unicode(key)) for key in sorted(conv.keys(), key = lambda item: - len(item)))) 31 return regex.sub(lambda match: conv[match.group()], text) 32 33def generate_airplane_latex(source): 34 """ 35 Generates the LaTeX files and directory structure for the procedures 36 """ 37 root = os.path.join("procedures", source['name']) 38 checklists_path = os.path.join(root, "checklists") 39 if(not os.path.isdir(root)): 40 os.mkdir(root) 41 if(not os.path.isdir(checklists_path)): 42 os.mkdir(checklists_path) 43 44 # Write some warning notes 45 preamble = open(os.path.join(root, "preamble.tex"), "w") 46 47 preamble.write("\\section{Preamble}") 48 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.") 49 50 documentation_tex = open(os.path.join(root, source['dir'] + "_documentation.tex"), "w") 51 documentation_tex.write("\\documentclass{article}\n") 52 documentation_tex.write("\\usepackage{hyperref}\n\usepackage{graphicx}\n" 53 "\\usepackage{fancyhdr}\\pagestyle{fancy}\n\\lfoot{Intended for FlightGear}\\rfoot{Not for real world use!}") 54 documentation_tex.write("\\title{"+source['name']+" documentation}\n") 55 documentation_tex.write("\\author{FlightGear team}\n") 56 documentation_tex.write("\\renewcommand{\\familydefault}{\\sfdefault}") 57 documentation_tex.write("\\begin{document}\n") 58 documentation_tex.write("\\maketitle\n") 59 60 checklist_tex = open(os.path.join(root, source['dir'] + "_checklist.tex"), "w") 61 checklist_tex.write("\\documentclass{article}\n") 62 checklist_tex.write("\\usepackage{fancyhdr}\\pagestyle{fancy}\n\\lfoot{Intended for FlightGear}\\rfoot{Not for real world use!}") 63 checklist_tex.write("\\begin{document}\n") 64 65 # If there's a thumbnail available, copy it into the documentation and include it 66 # just before the beginning of the document, just after the title. 67 if(source['thumbnail']): 68 shutil.copyfile(FG_ROOT + "Aircraft/" + source['dir'] + "/thumbnail.jpg", 69 os.path.join(root, "thumbnail.jpg")) 70 documentation_tex.write("\\begin{figure}[h!]\\centering") 71 documentation_tex.write("\\includegraphics[width=5cm,height=5cm,keepaspectratio]{thumbnail.jpg}") 72 documentation_tex.write("\\end{figure}") 73 documentation_tex.write("\\tableofcontents\n") 74 documentation_tex.write("\\input{preamble.tex}\n") 75 76 # If any additional documentation, copy it to the procedures directory and 77 # add it to the procedures.tex file 78 if source['extra_documentation']: 79 extra_docs_dir = os.path.join(FG_ROOT + "Aircraft/" + source['dir'], "Docs") 80 extra_docs_dest_dir = os.path.join("procedures/" + source['name'], "Docs") 81 if os.path.isdir(extra_docs_dest_dir): 82 shutil.rmtree(extra_docs_dest_dir) 83 shutil.copytree(extra_docs_dir, extra_docs_dest_dir) 84 documentation_tex.write("\\input{Docs/" + source['dir'] + "_documentation}\n") 85 86 # If there are any checklist files parsed, write a title 87 if(source['sources'] > 0): 88 documentation_tex.write("\\input{checklists.tex}\n") 89 checklists_tex = open(os.path.join(root, "checklists.tex"), "w") 90 checklists_tex.write("\\section{Checklists}\n") 91 92 # and the checklists themselves. 93 for xmlfile in source['sources']: 94 tree = etree.parse(xmlfile) 95 chkl_root = tree.getroot() 96 for checklist in chkl_root: 97 if checklist.tag != "checklist": 98 print "Unrecognised tag in", xmlfile 99 continue 100 title = checklist.find("title") 101 if title is None: 102 title = "Untitled" 103 else: 104 title = tex_escape(title.text) 105 items = [] 106 107 for item in checklist.findall("item"): 108 name = item.find("name") 109 value = item.find("value") 110 if name is None or value is None: 111 continue 112 if name.text is None or value.text is None: 113 continue 114 items.append({ 115 'name': tex_escape(name.text), 116 'value': tex_escape(value.text) 117 }) 118 filename = title + ".tex" 119 filename = filename.replace("/", "_") 120 filename = filename.replace(" ", "_") 121 checklists_tex.write("\\subsection{" + title + "}\n") 122 checklist_tex.write("\\section*{" + title + "}\n") 123 checklists_tex.write("\\input{checklists/"+filename+"}\n") 124 checklist_tex.write("\\input{checklists/"+filename+"}\n") 125 f = open(os.path.join(checklists_path,filename), "w") 126 if len(items) > 0: 127 f.write("\\begin{description}\n") 128 for item in items: 129 f.write("\\item["+item['name']+"] \dotfill " + item['value']+"\n") 130 f.write("\\end{description}\n") 131 checklist_tex.write("\\clearpage\n") 132 133 documentation_tex.write("\\end{document}\n") 134 checklist_tex.write("\\end{document}\n") 135 136def gather_aircraft_metadata(directory = FG_ROOT + "Aircraft"): 137 aircrafts = [] 138 for aircraft_directory in os.listdir(directory): 139 for rootfile in os.listdir(os.path.join(directory, aircraft_directory)): 140 rootfile = os.path.join(os.path.join(directory, aircraft_directory), rootfile) 141 if not (os.path.isfile(rootfile) and rootfile.endswith("-set.xml")): 142 continue 143 # Houston, we found a set.xml. Let's read it. This implies an aircraft 144 tree = etree.parse(rootfile) 145 name = aircraft_directory 146 147 148 for descr in tree.iter('description'): 149 name = descr.text 150 break 151 aircraft = { 152 'sources' : [], 153 'name' : name, 154 'dir' : aircraft_directory, 155 'thumbnail': False, 156 'extra_documentation': False 157 } 158 # Check if the aircraft provides additional documentation 159 aircraft['extra_documentation'] = os.path.isdir(os.path.join(directory, 160 aircraft_directory 161 + "/Docs/")) & os.path.isfile(os.path.join(directory, aircraft_directory 162 + "/Docs/" 163 + aircraft_directory 164 + "_documentation.tex")) 165 # Check if there is a thumbnail 166 if os.path.isfile(os.path.join(directory, aircraft_directory + "/thumbnail.jpg")): 167 aircraft['thumbnail'] = True 168 169 for checklist in tree.iter('checklists'): 170 aircraft['sources'].append(os.path.join(directory, aircraft_directory, checklist.attrib['include'])) 171 if 'include' in tree.getroot().attrib: 172 try: 173 tree = etree.parse(os.path.join(directory, aircraft_directory) + "/" + tree.getroot().attrib['include']) 174 for checklist in tree.iter('checklists'): 175 if 'include' in checklist.attrib: 176 aircraft['sources'].append(os.path.join(directory, aircraft_directory, checklist.attrib['include'])) 177 except: 178 pass 179 180 181 182 if(len(aircraft['sources']) > 0 or aircraft['extra_documentation']): 183 aircrafts.append(aircraft) 184 185 # Check if there are checklists that can be parsed 186 # for root, dirs, files in os.walk(os.path.join(directory, aircraft_directory), topdown=False): 187 # for name in files: 188 # if(name.endswith("checklists.xml")): 189 # aircraft['sources'].append(os.path.join(root,name)) 190 191 return aircrafts 192 193 194def compile_airplane_latex(source): 195 """ 196 Compile the procedures using pdflatex 197 """ 198 wd = os.getcwd() 199 root = os.path.join("procedures", source['name']) 200 os.chdir(root) 201 with open(os.devnull, "w") as fnull: 202 mainfile = source['dir'] + "_documentation.tex" 203 call(["pdflatex", "-interaction", "nonstopmode", mainfile], stdout=fnull,stderr=fnull) 204 call(["pdflatex", "-interaction", "nonstopmode", mainfile], stdout=fnull,stderr=fnull) 205 mainfile = source['dir'] + "_checklist.tex" 206 call(["pdflatex", "-interaction", "nonstopmode", mainfile], stdout=fnull,stderr=fnull) 207 call(["pdflatex", "-interaction", "nonstopmode", mainfile], stdout=fnull,stderr=fnull) 208 os.chdir(wd) 209 210 211def generate_airplane_documentation(): 212 # First generate the index of what should be included in the 213 # documentation 214 aircrafts = gather_aircraft_metadata("../Aircraft") 215 if(len(aircrafts) == 0 ): 216 print "No aircraft found; wrong directory?" 217 if(not os.path.isdir("procedures")): 218 os.mkdir("procedures") 219 220 # Then generate the documentation per airplane 221 for aircraft in aircrafts: 222 generate_airplane_latex(aircraft) 223 compile_airplane_latex(aircraft) 224 225# The main procedure 226generate_airplane_documentation() 227