First version of Grafting a Tree book generator.

master
Gijs 4 years ago
parent 30bd684622
commit 628b681940

@ -3,4 +3,6 @@
# Installation # Installation
The prototype uses pagedjs-cli to convert the HTML into PDF. In a terminal `cd` into the cloned folder and run: The prototype uses pagedjs-cli to convert the HTML into PDF. In a terminal `cd` into the cloned folder and run:
`npm install pagedjs-cli` `npm install pagedjs-cli`
*to do* Add instructions on installation of NLTK data

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(' ', '&nbsp;').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

@ -37,20 +37,8 @@ from trees import trees
import os import os
import os.path import os.path
from colored import fg, attr import sys
from io import StringIO
## Text attributes
bold = attr(1)
underlined = (4)
reset = attr(0)
## Text colors
black = fg(0)
spring_green = fg(48)
light_gray = fg(7)
sky_blue = fg(109)
yellow = fg(3)
def choose_a_tree(dict): def choose_a_tree(dict):
collection = random.choice(list(trees.items())) collection = random.choice(list(trees.items()))
@ -77,7 +65,7 @@ def bio(gardener):
bio = wikipedia.page(gardener) bio = wikipedia.page(gardener)
short_bio = bio.summary short_bio = bio.summary
except: except:
short_bio = "{}There is no English Wikipedia page about this person.{}".format(sky_blue, reset) short_bio = "There is no English Wikipedia page about this person."
return short_bio return short_bio
def bio_offline(gardener): def bio_offline(gardener):
@ -87,7 +75,7 @@ def bio_offline(gardener):
with open(filename, 'r') as h: with open(filename, 'r') as h:
return h.read() return h.read()
return "{}There is no English Wikipedia page about this person.{}".format(sky_blue, reset) return "There is no English Wikipedia page about this person."
""" """
@ -106,52 +94,54 @@ def grow_new_branch (bud):
# Turn nltk tree back into a sentence # Turn nltk tree back into a sentence
def trim_tree (branches): def trim_tree (branches):
tree = " ".join([branch[0] for branch in branches]) tree = " ".join([branch[0] for branch in branches])
# Remove spaces in html tags
tree = re.sub(r'\<\s*(\/?\w+)\s*\>', '<\\1>', tree)
return re.sub(r'\s[\.,:;\(\)]', '', tree) return re.sub(r'\s[\.,:;\(\)]', '', tree)
def show (state, length=1): def graft_a_tree (years = 3):
os.system('clear') gardener, source, tree = choose_a_tree(trees)
# Optionally vertically align here short_bio = bio_offline(gardener)
print(state)
time.sleep(length) print(
'<section>'
def grow (tree, generation = 1): + 'Using a quote by <em>{}</em><br><br>'.format(gardener) \
branches = nltk.pos_tag(word_tokenize(tree)) + short_bio
# Filter out nouns, pick one + '</section>'
position, bud = random.choice([(position, bud[0]) for position, bud in enumerate(branches) if bud[1] == 'NN']) )
# Make new tree placeholder for the bud show we can show it in two states and then grow out year = 1
next_tree = '{} {{}} {}'.format(trim_tree(branches[:max(0, position)]), trim_tree(branches[position+1:]))
new_branch = grow_new_branch(bud) print('<p class="tree first">{}</p>'.format(tree))
show(tree, 3 if generation == 1 else 1) while year <= years:
# Mark the bud, underlined and orange branches = nltk.pos_tag(word_tokenize(tree))
show(next_tree.format("{}{}{}{}{}".format(bold, yellow, bud, reset, black)), 2) # Filter out nouns, pick one
# Mark the new branch green position, bud = random.choice([(position, bud[0]) for position, bud in enumerate(branches) if bud[1] == 'NN'])
show(next_tree.format("{}{}{}".format(spring_green, new_branch, black)), 2)
show(next_tree.format("{}{}{}".format(fg(82), new_branch, black)), 1) tree_with_bud = '{} <em>{}</em> {}'.format(
show(next_tree.format("{}{}{}".format(fg(120), new_branch, black)), 1) trim_tree(branches[:max(0, position)]),
show(next_tree.format("{}{}{}".format(fg(157), new_branch, black)), 1) bud.strip(),
show(next_tree.format("{}{}{}".format(fg(195), new_branch, black)), 1) trim_tree(branches[position+1:]))
# Let it grow again
print('<p class="tree" data-year="{}">{}</p>'.format(year, tree_with_bud))
return next_tree.format(new_branch)
# Make new tree placeholder for the bud show we can show it in two states and then grow out
grown_tree = '{} <strong>{}</strong> {}'.format(
trim_tree(branches[:max(0, position)]),
grow_new_branch(bud).strip(),
trim_tree(branches[position+1:]))
# Let it grow again
print('<p class="tree" data-year="{}">{}</p>'.format(year, grown_tree))
grown_tree = re.sub(r'\<\/?\w+\>', '', grown_tree)
if grown_tree == tree:
break
else:
tree = grown_tree
year += 1
if __name__ == '__main__': if __name__ == '__main__':
while True: graft_a_tree(3)
gardener, source, tree = choose_a_tree(trees)
short_bio = bio_offline(gardener)
show(
'Using a quote by {}{}{}\n\n'.format(bold, gardener, reset) \
+ short_bio, 3
)
generation = 1
while len(tree) < 1500:
next_tree = grow(tree, generation)
if next_tree == tree:
break
else:
tree = next_tree
generation += 1

@ -65,7 +65,7 @@ def make_pdf (html, path_out=None, extra_scripts=[]):
shutil.copy(name_out, path_out) shutil.copy(name_out, path_out)
return path_out return path_out
else: else:
with open(name_out) as generated_pdf: with open(name_out, 'rb') as generated_pdf:
return generated_pdf.read() return generated_pdf.read()
if __name__ == '__main__': if __name__ == '__main__':

@ -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…
Cancel
Save