add possibility for outpit file, parameterize report.gj
[talweg.git] / reports / ipynb_generator.py
1 #!/usr/bin/env python
2
3 import sys, os, re
4
5 # Languages mapping as used by markdown/pandoc
6 shortname2language = dict(
7 c='C',
8 cpp='Cpp',
9 f='Fortran',
10 html='HTML',
11 js='JavaScript',
12 r='R',
13 rb='Ruby',
14 pl='Perl',
15 py='Python',
16 sh='Bash',
17 tex='Tex',
18 )
19
20 def read(text, argv=sys.argv[3:]):
21 lines = text.splitlines()
22 # First read all include statements
23 for i in range(len(lines)):
24 if lines[i].startswith('#include "'):
25 filename = lines[i].split('"')[1]
26 with open(filename, 'r') as f:
27 include_text = f.read()
28 lines[i] = include_text
29 text = '\n'.join(lines)
30
31 # Run Mako
32 mako_kwargs = {}
33 for arg in argv:
34 key, value = arg.split('=')
35 mako_kwargs[key] = value
36
37 try:
38 import mako
39 has_mako = True
40 except ImportError:
41 print('Cannot import mako - mako is not run')
42 has_mako = False
43
44 if has_mako:
45 from mako.template import Template
46 from mako.lookup import TemplateLookup
47 lookup = TemplateLookup(directories=[os.curdir])
48 # text = text.encode('utf-8')
49 temp = Template(text=text, lookup=lookup, strict_undefined=True)
50 text = temp.render(**mako_kwargs)
51
52 # Parse the cells
53 lines = text.splitlines()
54 cells = []
55 inside = None # indicates which type of cell we are inside
56 fullname = None # full language name in code cells
57 for line in lines:
58 if line.startswith('-----'):
59 # New cell, what type?
60 m = re.search(r'-----([a-z0-9-]+)?', line)
61 if m:
62 shortname = m.group(1)
63 if shortname:
64 # Check if code is to be typeset as static
65 # Markdown code (e.g., shortname=py-t)
66 .format(shortname))
67 astext = shortname[-2:] == '-t'
68 .format(astext, shortname))
69 if astext:
70 # Markdown
71 shortname = shortname[:-2]
72 inside = 'markdown'
73 cells.append(['markdown', 'code', ['\n']])
74 cells[-1][2].append('```%s\n' % fullname)
75 else:
76 # Code cell
77 if shortname in shortname2language:
78 fullname = shortname2language[shortname]
79 inside = 'codecell'
80 cells.append(['codecell', fullname, []])
81 else:
82 # Markdown cell
83 inside = 'markdown'
84 cells.append(['markdown', 'text', ['\n']])
85 else:
86 raise SyntaxError('Wrong syntax of cell delimiter:\n{}'
87 .format(repr(line)))
88 else:
89 # Ordinary line in a cell
90 if inside in ('markdown', 'codecell'):
91 cells[-1][2].append(line)
92 else:
93 raise SyntaxError('line\n {}\nhas no beginning cell delimiter'
94 .format(line))
95 # Merge the lines in each cell to a string
96 for i in range(len(cells)):
97 if cells[i][0] == 'markdown' and cells[i][1] == 'code':
98 # Add an ending ``` of code
99 cells[i][2].append('```\n')
100 cells[i][2] = '\n'.join(cells[i][2])
101 return cells
102
103 def write(cells):
104 """Turn cells list into valid IPython notebook code."""
105 # Use Jupyter nbformat functionality for writing the notebook
106
107 from nbformat.v4 import (
108 new_code_cell, new_markdown_cell, new_notebook, writes)
109 nb_cells = []
110
111 for cell_tp, language, block in cells:
112 if cell_tp == 'markdown':
113 nb_cells.append(
114 new_markdown_cell(source=block))
115 elif cell_tp == 'codecell':
116 nb_cells.append(new_code_cell(source=block))
117
118 nb = new_notebook(cells=nb_cells)
119 filestr = writes(nb)
120 return filestr
121
122 def driver():
123 """Compile a document and its variables."""
124 try:
125 inputfile = sys.argv[1]
126 with open(filename, 'r') as f:
127 text = f.read()
128 outputfile = '-' if len(sys.argv) <= 2 else sys.argv[2]
129 except (IndexError, IOError) as e:
130 print('Usage: %s inputfile [outputfile|- [Mako args]]' % (sys.argv[0]))
131 print(e)
132 sys.exit(1)
133 cells = read(text, argv=sys.argv[3:])
134 filestr = write(cells)
135 # Assuming file extension .gj (generate Jupyter); TODO: less strict
136 outputfile = inputfile[:-3]+'.ipynb' if outputfile == '-' else outputfile
137 with open(filename, 'w') as f:
138 f.write(filestr)
139
140 if __name__ == '__main__':
141 driver()