First version of Grafting a Tree book generator.
							parent
							
								
									30bd684622
								
							
						
					
					
						commit
						628b681940
					
				
											
												Binary file not shown.
											
										
									
								| @ -0,0 +1,48 @@ | ||||
| #!/usr/bin/env/ python | ||||
| from sys import stdout | ||||
| from flask import Flask, Response, render_template, request | ||||
| from pagedjs import make_pdf | ||||
| from graft_a_tree import graft_a_tree | ||||
| from io import StringIO | ||||
| import sys | ||||
| import textwrap | ||||
| 
 | ||||
| BASEURL = '' | ||||
| MIN_GROW_YEARS = 0 | ||||
| MAX_GROW_YEARS = 25 | ||||
| 
 | ||||
| app = Flask(__name__) | ||||
| 
 | ||||
| def wrap (text, width): | ||||
|   return'\n'.join([(' \\\n').join(textwrap.wrap(line, width=width - 2)) for line in text.splitlines()]) | ||||
| 
 | ||||
| with open('graft_a_tree.py', 'r') as script: | ||||
|   source_code = script.read() | ||||
| 
 | ||||
| @app.route('{}/'.format(BASEURL)) | ||||
| def index(): | ||||
|   return render_template('index.html', BASEURL=BASEURL) | ||||
| 
 | ||||
| @app.route('{}/graft'.format(BASEURL), methods=['POST']) | ||||
| def graft(): | ||||
|   years = max(MIN_GROW_YEARS, min(MAX_GROW_YEARS, int(request.form['years']))) | ||||
| 
 | ||||
|   with StringIO() as script_stdout: | ||||
|     default_stdout = sys.stdout | ||||
|     sys.stdout = script_stdout | ||||
|     graft_a_tree(years) | ||||
|     content = script_stdout.getvalue() | ||||
|     sys.stdout = default_stdout | ||||
| 
 | ||||
|   html = render_template('book.html', content=content, source_code=wrap(source_code, 100).replace(' ', ' ').replace('\n', '<br />')) | ||||
|   # print(html) | ||||
|   print('html rendered') | ||||
|   pdf = make_pdf(html) | ||||
|   print('made pdf') | ||||
|   r = Response(pdf, mimetype='application/pdf') | ||||
| 
 | ||||
|   r.headers.extend({ | ||||
|     'Content-Disposition': 'attachment; filename="grafted-tree.pdf"' | ||||
|   }) | ||||
| 
 | ||||
|   return r | ||||
| @ -0,0 +1,3 @@ | ||||
| nltk | ||||
| wikipedia | ||||
| flask | ||||
| @ -0,0 +1,3 @@ | ||||
| export FLASK_APP=app.py | ||||
| export FLASK_ENV=development | ||||
| flask run | ||||
| @ -0,0 +1,53 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|   <meta charset="UTF-8"> | ||||
|   <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|   <title>Document</title> | ||||
|   <style> | ||||
|     body, html { | ||||
|       font-size: 120%; | ||||
|     } | ||||
| 
 | ||||
|     h1 { | ||||
|       page-break-after: right; | ||||
|     } | ||||
| 
 | ||||
|     .tree{ | ||||
|       page-break-before: always; | ||||
|       page: tree; | ||||
|     } | ||||
| 
 | ||||
|     code { | ||||
|       page-break-before: right; | ||||
|       font-size: 57.5%; | ||||
|       line-height: 1.2; | ||||
|     } | ||||
| 
 | ||||
|     .tree.first { | ||||
|       page-break-before: right; | ||||
|       page: initial; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     .tree[data-year] { | ||||
|       string-set: year attr(data-year); | ||||
|     } | ||||
| 
 | ||||
|     @page { | ||||
|       size: a5 portrait; | ||||
|     } | ||||
| 
 | ||||
|   </style> | ||||
| </head> | ||||
| <body> | ||||
|   <h1>Grafted tree</h1> | ||||
| 
 | ||||
|   {% autoescape false %} | ||||
|   {{ content }} | ||||
| 
 | ||||
|   <code>{{ source_code }}</code> | ||||
|   {% endautoescape %} | ||||
| </body> | ||||
| </html> | ||||
| @ -0,0 +1,97 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|   <meta charset="UTF-8"> | ||||
|   <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|   <title>Document</title> | ||||
|   <style> | ||||
|     [data-message] { | ||||
|       display: none; | ||||
|       font-size: var(--font-size-smaller); | ||||
|       line-height: var(--line-height--smaller); | ||||
|       font-style: italic; | ||||
|       margin: var(--line-height--smaller) 0; | ||||
|     } | ||||
| 
 | ||||
|     [data-message][data-active] { | ||||
|       display: block; | ||||
|     } | ||||
| 
 | ||||
|   </style> | ||||
| </head> | ||||
| <body> | ||||
|   <h1>Grafting a tree</h1> | ||||
|   <p> | ||||
|     <form action="{{ BASEURL }}/graft" method="POST"> | ||||
|       Graft a tree for <input type="number" name="years" value="3" max="25" min="0" size="4" /> years. | ||||
|       <button type="submit">Graft</button> | ||||
|     </form> | ||||
|   </p> | ||||
|   <p data-message="working"> | ||||
|     Grafting your tree. This might take a while. | ||||
|   </p> | ||||
|   <p data-message="error"> | ||||
|     Something went wrong. | ||||
|   </p> | ||||
|   <script> | ||||
|     function getFilename (headers, fallback) { | ||||
|       if (headers.has('Content-Disposition')) { | ||||
|         header = headers.get('Content-Disposition') | ||||
|         matches = header.match(/filename="(.+)"/) | ||||
|          | ||||
|         if (matches.length == 2) { | ||||
|           return matches[1] | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       return fallback; | ||||
|     } | ||||
| 
 | ||||
|     (function () { | ||||
|       if (fetch) { | ||||
|         var form = document.querySelector('form'), | ||||
|             button = form.querySelector('button'), | ||||
|             messageWorking = document.querySelector('[data-message="working"]'), | ||||
|             messageError = document.querySelector('[data-message="error"]'); | ||||
| 
 | ||||
|         form.addEventListener('submit', function (e) { | ||||
|           e.preventDefault(); | ||||
|           button.disabled = true; | ||||
|           delete messageError.dataset.active; | ||||
|           messageWorking.dataset.active = true; | ||||
|           const data = new FormData(form); | ||||
|    | ||||
|           fetch(form.action, { | ||||
|             method: "POST", | ||||
|             body: data | ||||
|           }).then(function (r) { | ||||
|             console.log(r); | ||||
|             r.blob().then(function (blob) { | ||||
|               var filename = getFilename(r.headers, 'Grafted-tree.pdf'); | ||||
|               const a = document.createElement('a'); | ||||
|               a.setAttribute('href', URL.createObjectURL(blob)); | ||||
|               a.setAttribute('download', filename); | ||||
|                | ||||
|               if (document.createEvent) { | ||||
|                 const event = document.createEvent('MouseEvents'); | ||||
|                 event.initEvent('click', true, true); | ||||
|                 a.dispatchEvent(event); | ||||
|               } | ||||
|               else { | ||||
|                 a.click(); | ||||
|               } | ||||
|               delete messageWorking.dataset.active; | ||||
|               button.disabled = false; | ||||
|             }); | ||||
|           }).catch(function() { | ||||
|             delete messageWorking.dataset.active; | ||||
|             messageError.dataset.active = true; | ||||
|             button.disabled = false; | ||||
|           }); | ||||
|         }); | ||||
|       } | ||||
|     })(); | ||||
|   </script> | ||||
| </body> | ||||
| </html> | ||||
					Loading…
					
					
				
		Reference in New Issue