Browse Source

adding a flask web interface, closely linked to the pad2pdf interfaces

master
manetta 3 years ago
parent
commit
893c2a6631
  1. 0
      command-line/Makefile
  2. 0
      command-line/css/interface.css
  3. 0
      command-line/css/print.css
  4. 0
      command-line/js/paged.js
  5. 0
      command-line/js/paged.polyfill.js
  6. 0
      command-line/template.debug.html
  7. 0
      command-line/template.html
  8. 0
      command-line/update.py
  9. 13
      web-interface/Makefile
  10. 3
      web-interface/requirements.txt
  11. 180
      web-interface/static/css/interface.css
  12. 52
      web-interface/static/css/main.css
  13. 31061
      web-interface/static/js/paged.js
  14. 31107
      web-interface/static/js/paged.polyfill.js
  15. 34
      web-interface/templates/flask/base.html
  16. 19
      web-interface/templates/flask/index.html
  17. 9
      web-interface/templates/flask/inspect.html
  18. 27
      web-interface/templates/flask/pagedjs.html
  19. 6
      web-interface/templates/flask/stylesheet.html
  20. 15
      web-interface/templates/local/template.html
  21. 12
      web-interface/templates/local/template.inspect.html
  22. 155
      web-interface/update.py
  23. 65
      web-interface/web-interface.py

0
Makefile → command-line/Makefile

0
css/interface.css → command-line/css/interface.css

0
css/print.css → command-line/css/print.css

0
js/paged.js → command-line/js/paged.js

0
js/paged.polyfill.js → command-line/js/paged.polyfill.js

0
template.debug.html → command-line/template.debug.html

0
template.html → command-line/template.html

0
update.py → command-line/update.py

13
web-interface/Makefile

@ -0,0 +1,13 @@
all: run
run:
python3 -m http.server
update:
python3 update.py
upload:
scp -r ./static/Unfolded.html ./static/css/ ./static/js/ ./static/fonts/ varia:public_html/volumetric-regimes/
uploadimages:
scp -r ./static/images/ varia:public_html/volumetric-regimes/

3
web-interface/requirements.txt

@ -0,0 +1,3 @@
flask
etherpump
weasyprint

180
web-interface/static/css/interface.css

@ -0,0 +1,180 @@
/* CSS for Paged.js interface – v0.2 */
/* Change the look */
:root {
--color-background: whitesmoke;
--color-pageSheet: #cfcfcf;
--color-pageBox: violet;
--color-paper: white;
--color-marginBox: transparent;
--pagedjs-crop-color: black;
--pagedjs-crop-shadow: white;
--pagedjs-crop-stroke: 1px;
}
/* To define how the book look on the screen: */
@media screen {
body {
background-color: var(--color-background);
}
.pagedjs_pages {
display: flex;
width: calc(var(--pagedjs-width) * 2);
flex: 0;
flex-wrap: wrap;
margin: 0 auto;
}
.pagedjs_page {
background-color: var(--color-paper);
box-shadow: 0 0 0 1px var(--color-pageSheet);
margin: 0;
flex-shrink: 0;
flex-grow: 0;
margin-top: 10mm;
}
.pagedjs_first_page {
margin-left: var(--pagedjs-width);
}
.pagedjs_page:last-of-type {
margin-bottom: 10mm;
}
.pagedjs_pagebox{
box-shadow: 0 0 0 1px var(--color-pageBox);
}
.pagedjs_left_page{
z-index: 20;
width: calc(var(--pagedjs-bleed-left) + var(--pagedjs-pagebox-width))!important;
}
.pagedjs_left_page .pagedjs_bleed-right .pagedjs_marks-crop {
border-color: transparent;
}
.pagedjs_left_page .pagedjs_bleed-right .pagedjs_marks-middle{
width: 0;
}
.pagedjs_right_page{
z-index: 10;
position: relative;
left: calc(var(--pagedjs-bleed-left)*-1);
}
/* show the margin-box */
.pagedjs_margin-top-left-corner-holder,
.pagedjs_margin-top,
.pagedjs_margin-top-left,
.pagedjs_margin-top-center,
.pagedjs_margin-top-right,
.pagedjs_margin-top-right-corner-holder,
.pagedjs_margin-bottom-left-corner-holder,
.pagedjs_margin-bottom,
.pagedjs_margin-bottom-left,
.pagedjs_margin-bottom-center,
.pagedjs_margin-bottom-right,
.pagedjs_margin-bottom-right-corner-holder,
.pagedjs_margin-right,
.pagedjs_margin-right-top,
.pagedjs_margin-right-middle,
.pagedjs_margin-right-bottom,
.pagedjs_margin-left,
.pagedjs_margin-left-top,
.pagedjs_margin-left-middle,
.pagedjs_margin-left-bottom {
box-shadow: 0 0 0 1px inset var(--color-marginBox);
}
/* uncomment this part for recto/verso book : ------------------------------------ */
/*
.pagedjs_pages {
flex-direction: column;
width: 100%;
}
.pagedjs_first_page {
margin-left: 0;
}
.pagedjs_page {
margin: 0 auto;
margin-top: 10mm;
}
.pagedjs_left_page{
width: calc(var(--pagedjs-bleed-left) + var(--pagedjs-pagebox-width) + var(--pagedjs-bleed-left))!important;
}
.pagedjs_left_page .pagedjs_bleed-right .pagedjs_marks-crop{
border-color: var(--pagedjs-crop-color);
}
.pagedjs_left_page .pagedjs_bleed-right .pagedjs_marks-middle{
width: var(--pagedjs-cross-size)!important;
}
.pagedjs_right_page{
left: 0;
}
*/
/*--------------------------------------------------------------------------------------*/
/* uncomment this par to see the baseline : -------------------------------------------*/
/*
.pagedjs_pagebox {
--pagedjs-baseline: 22px;
--pagedjs-baseline-position: 5px;
--pagedjs-baseline-color: cyan;
background: linear-gradient(transparent 0%, transparent calc(var(--pagedjs-baseline) - 1px), var(--pagedjs-baseline-color) calc(var(--pagedjs-baseline) - 1px), var(--pagedjs-baseline-color) var(--pagedjs-baseline)), transparent;
background-size: 100% var(--pagedjs-baseline);
background-repeat: repeat-y;
background-position-y: var(--pagedjs-baseline-position);
} */
/*--------------------------------------------------------------------------------------*/
}
/* Marks (to delete when merge in paged.js) */
.pagedjs_marks-crop{
z-index: 999999999999;
}
.pagedjs_bleed-top .pagedjs_marks-crop,
.pagedjs_bleed-bottom .pagedjs_marks-crop{
box-shadow: 1px 0px 0px 0px var(--pagedjs-crop-shadow);
}
.pagedjs_bleed-top .pagedjs_marks-crop:last-child,
.pagedjs_bleed-bottom .pagedjs_marks-crop:last-child{
box-shadow: -1px 0px 0px 0px var(--pagedjs-crop-shadow);
}
.pagedjs_bleed-left .pagedjs_marks-crop,
.pagedjs_bleed-right .pagedjs_marks-crop{
box-shadow: 0px 1px 0px 0px var(--pagedjs-crop-shadow);
}
.pagedjs_bleed-left .pagedjs_marks-crop:last-child,
.pagedjs_bleed-right .pagedjs_marks-crop:last-child{
box-shadow: 0px -1px 0px 0px var(--pagedjs-crop-shadow);
}

52
web-interface/static/css/main.css

@ -0,0 +1,52 @@
body{
background-color: #a6e89d;
margin: 1vh 5vw 2vh 5vw;
z-index: 1;
}
div#nav{
position: fixed;
width: calc(100% - 2em);
margin: 1em;
text-align: right;
left: 0;
top: 0;
z-index: 999;
}
div#nav a#home,
div#nav span#loading,
div#nav a#update{
float: left;
padding-right: 0.25em;
}
div#nav span#loading{
display: none;
margin: 0.35em 0.25em;
color: fuchsia;
opacity: 0;
animation: fade 2s linear;
}
@keyframes fade {
0%,100% { opacity: 0 }
50% { opacity: 1 }
}
iframe{
width: 90vw;
height: 88vh;
}
input{
min-width: 300px;
}
div#index ul{
padding: 0;
margin: 0 0 0 5em;
width: 750px;
}
div#index ul li{
margin-bottom: 1em;
list-style: none;
}
div#index ul li::before{
content: "▁ ▂ ▃ ▄ ▅ ▆ ▇ █";
float: left;
margin-left: -9.5em;
}

31061
web-interface/static/js/paged.js

File diff suppressed because it is too large

31107
web-interface/static/js/paged.polyfill.js

File diff suppressed because it is too large

34
web-interface/templates/flask/base.html

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset="utf-8" />
<title>{{ title }}</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/main.css')}}">
{% block head %}
{% endblock %}
</head>
<body>
{% block nav %}
{% endblock %}
<div id="wrapper">
{% block content %}
{% endblock %}
</div>
</body>
<script>
window.addEventListener('load', function () {
// Insert the nav buttons, after the page is loaded
const nav = document.createElement('div');
nav.id = 'nav';
nav.innerHTML = '<a id="home" href="/"><button>/</button></a> <a id="update" href="/update/" onclick="load()"><button>update</button></a><span id="loading">loading</span><a id="pdf" href="/pagedjs/"><button>pdf</button></a> <a id="inspect" href="/inspect/"><button>inspect</button></a> <a id="stylesheet" href="/stylesheet/"><button>stylesheet</button></a>: <input type="text" name="pad" value="https://pad.vvvvvvaria.org/volumetric-regimes.css">';
document.body.insertBefore(nav, document.body.firstChild)
})
function load(){
const loading = document.getElementById("loading");
loading.style.display = 'inline-block';
}
</script>
{% block footer %}
{% endblock %}
</html>

19
web-interface/templates/flask/index.html

@ -0,0 +1,19 @@
{% extends "flask/base.html" %}
{% block content %}
<div id="index">
<br>
<br>
<h1>Volumetric Regimes</h1>
<h1>Shared Book/PDF rendering environment</h1>
<br>
<br>
<ul>
<li><strong>update</strong>: Update the materials from the <a href="https://possiblebodies.constantvzw.org/book/index.php?title=Unfolded" target="new">Unfolded wiki page</a> (saved as <a href="{{ url_for('static', filename='Unfolded.html')}}">Unfolded.html</a>)</li>
<li><strong>pdf</strong>: Preview the Book/PDF file, which is displayed in the browser using Paged.js</li>
<li><strong>inspect</strong>: Dive into the <a href="{{ url_for('static', filename='Unfolded.html')}}">Unfolded.html</a> webpage that renders the Book/PDF</li>
<li><strong>stylesheet</strong>: Change the layout of the Book/PDF (saved as <a href="{{ url_for('static', filename='css/volumetric-regimes.css')}}">volumetric-regimes.css</a>)</li>
</ul>
</div>
{% endblock %}

9
web-interface/templates/flask/inspect.html

@ -0,0 +1,9 @@
{% extends "flask/base.html" %}
{% block head %}
<link href="{{ url_for('static', filename='css/volumetric-regimes.css')}}" rel="stylesheet" type="text/css" media="screen">
{% endblock %}
{% block content %}
{{ publication_unfolded | safe }}
{% endblock %}

27
web-interface/templates/flask/pagedjs.html

@ -0,0 +1,27 @@
{% extends "flask/base.html" %}
{% block head %}
<script src="{{ url_for('static', filename='js/paged.js')}}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='js/paged.polyfill.js')}}" type="text/javascript"></script>
<link href="{{ url_for('static', filename='css/interface.css')}}" rel="stylesheet" type="text/css">
<link href="{{ url_for('static', filename='css/volumetric-regimes.css')}}" rel="stylesheet" type="text/css" media="print">
{% endblock %}
{% block content %}
{{ publication_unfolded | safe }}
{% endblock %}
{% block footer %}
<script>
window.addEventListener('load', function () {
// Add the main.css again, to load the stylesheet for the nav
var cssLink = document.createElement('link');
cssLink.rel = 'stylesheet';
cssLink.href = '/static/css/main.css';
var head = document.getElementsByTagName('head')[0];
head.insertBefore(cssLink, head.firstChild);
})
</script>
{% endblock %}

6
web-interface/templates/flask/stylesheet.html

@ -0,0 +1,6 @@
{% extends "flask/base.html" %}
{% block content %}
<iframe src="https://pad.vvvvvvaria.org/volumetric-regimes.css"></iframe>
{% endblock %}

15
web-interface/templates/local/template.html

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="./static/js/paged.js" type="text/javascript"></script>
<script src="./static/js/paged.polyfill.js" type="text/javascript"></script>
<link href="./static/css/interface.css" rel="stylesheet" type="text/css">
<link href="./static/css/print.css" rel="stylesheet" type="text/css" media="print">
</head>
<body>
<div id="wrapper">
{{ publication_unfolded }}
</div>
</body>
</html>

12
web-interface/templates/local/template.inspect.html

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="./static/css/print.css" rel="stylesheet" type="text/css" media="print">
</head>
<body>
<div id="wrapper">
{{ publication_unfolded }}
</div>
</body>
</html>

155
web-interface/update.py

@ -0,0 +1,155 @@
import urllib.request
import os
import re
import json
import jinja2
STATIC_FOLDER_PATH = './static' # without trailing slash
def API_request(url, pagename):
"""
url = API request url (string)
data = { 'query':
'pages' :
pageid : {
'links' : {
'?' : '?'
'title' : 'pagename'
}
}
}
}
"""
response = urllib.request.urlopen(url).read()
data = json.loads(response)
# Save response as JSON to be able to inspect API call
json_file = f'{ STATIC_FOLDER_PATH }/{ pagename }.json'
print('Saving JSON:', json_file)
with open(json_file, 'w') as out:
out.write(json.dumps(data, indent=4))
out.close()
return data
def download_media(html, images):
"""
html = string (HTML)
images = list of filenames (str)
"""
# check if 'images/' already exists
if not os.path.exists(f'{ STATIC_FOLDER_PATH }/images'):
os.makedirs(f'{ STATIC_FOLDER_PATH }/images')
# download media files
for filename in images:
filename = filename.replace(' ', '_') # safe filenames
# check if the image is already downloaded
# if not, then download the file
if not os.path.isfile(f'{ STATIC_FOLDER_PATH }/images/{ filename }'):
# first we search for the full filename of the image
url = f'{ wiki }/api.php?action=query&list=allimages&aifrom={ filename }&format=json'
response = urllib.request.urlopen(url).read()
data = json.loads(response)
# we select the first search result
# (assuming that this is the image we are looking for)
image = data['query']['allimages'][0]
# then we download the image
image_url = image['url']
image_filename = image['name']
print('Downloading:', image_filename)
image_response = urllib.request.urlopen(image_url).read()
# and we save it as a file
image_path = f'{ STATIC_FOLDER_PATH }/images/{ image_filename }'
out = open(image_path, 'wb')
out.write(image_response)
out.close()
import time
time.sleep(3) # do not overload the server
# replace src link
image_path = f'/{ STATIC_FOLDER_PATH }/images/{ filename }' # here the images need to link to the / of the domain, for flask :/// confusing! this breaks the whole idea to still be able to make a local copy of the file
html = re.sub(rf'src="/book/images/.*{ filename }"', f'src="{ image_path }"', html)
return html
def clean_up(html):
"""
html = string (HTML)
"""
html = re.sub(r'\[.*edit.*\]', '', html) # remove the [edit]
html = re.sub(r'href="/book/index.php\?title=', 'href="#', html) # remove the internal wiki links
return html
def parse_page(pagename, wiki):
"""
pagename = string
html = string (HTML)
"""
parse = f'{ wiki }/api.php?action=parse&page={ pagename }&pst=True&format=json'
data = API_request(parse, pagename)
# print(json.dumps(data, indent=4))
if 'parse' in data:
html = data['parse']['text']['*']
images = data['parse']['images']
html = download_media(html, images)
html = clean_up(html)
else:
html = None
return html
def save(html, pagename, publication_unfolded):
"""
html = string (HTML)
pagename = string
"""
if html:
# save final page that will be used with PagedJS
template_file = open(f'{ STATIC_FOLDER_PATH }/local/template.html').read()
template = jinja2.Template(template_file)
html = template.render(publication_unfolded=publication_unfolded, title=pagename)
html_file = f'{ STATIC_FOLDER_PATH }/{ pagename }.html'
print('Saving HTML:', html_file)
with open(html_file, 'w') as out:
out.write(html)
out.close()
# save extra html page for debugging
template_file = open(f'{ STATIC_FOLDER_PATH }/local/template.inspect.html').read()
template = jinja2.Template(template_file)
html = template.render(publication_unfolded=publication_unfolded, title=pagename)
html_file = f'{ STATIC_FOLDER_PATH }/{ pagename }.inspect.html'
print('Saving HTML:', html_file)
with open(html_file, 'w') as out:
out.write(html)
out.close()
def update_material_now(pagename, wiki):
"""
pagename = string
publication_unfolded = string (HTML)
"""
publication_unfolded = parse_page(pagename, wiki)
return publication_unfolded
# ---
if __name__ == "__main__":
wiki = 'https://possiblebodies.constantvzw.org/book' # remove tail slash '/'
pagename = 'Unfolded'
publication_unfolded = update_material_now(pagename, wiki) # download the latest version of the page
save(publication_unfolded, pagename, publication_unfolded) # save the page to file

65
web-interface/web-interface.py

@ -0,0 +1,65 @@
import flask
import urllib, json
import os
from update import *
# Create the application.
APP = flask.Flask(__name__)
# ---
PROJECT_NAME = 'volumetric-regimes'
DIR_PATH = '.' # without trailing slash
PORTNUMBER = 5522
WIKI = 'https://possiblebodies.constantvzw.org/book' # remove tail slash '/'
PAGENAME = 'Unfolded'
STYLESHEET = f'{ PROJECT_NAME }.css'
# ---
def download(STYLESHEET):
# using etherpump
os.system(f'{ DIR_PATH }/venv/bin/etherpump gettext { STYLESHEET } > { DIR_PATH }/static/css/{ STYLESHEET }')
@APP.route('/', methods=['GET'])
def pad():
if not os.path.exists(f'{ DIR_PATH }/static/Unfolded.html'):
download(STYLESHEET) # download the stylesheet pad
publication_unfolded = update_material_now(PAGENAME, WIKI) # download the latest version of the page
with open(f'{ DIR_PATH }/static/Unfolded.html', 'w') as out:
out.write(publication_unfolded) # save the html (without <head>) to file
else:
publication_unfolded = open(f'{ DIR_PATH }/static/Unfolded.html', 'r').read()
return flask.render_template('flask/index.html', title=PROJECT_NAME)
@APP.route('/update/', methods=['GET', 'POST'])
def update():
publication_unfolded = update_material_now(PAGENAME, WIKI) # download the latest version of the page
with open(f'{ DIR_PATH }/static/Unfolded.html', 'w') as out:
out.write(publication_unfolded) # save the html (without <head>) to file
return flask.render_template('flask/index.html', title=PROJECT_NAME)
@APP.route('/pagedjs/', methods=['GET', 'POST'])
def pagedjs():
download(STYLESHEET) # download the stylesheet pad
publication_unfolded = open(f'{ DIR_PATH }/static/Unfolded.html', 'r').read()
return flask.render_template('flask/pagedjs.html', publication_unfolded=publication_unfolded, STYLESHEET=STYLESHEET)
@APP.route('/inspect/', methods=['GET', 'POST'])
def inspect():
download(STYLESHEET) # download the stylesheet pad
publication_unfolded = open(f'{ DIR_PATH }/static/Unfolded.html', 'r').read()
return flask.render_template('flask/inspect.html', publication_unfolded=publication_unfolded, STYLESHEET=STYLESHEET)
@APP.route('/stylesheet/', methods=['GET'])
def stylesheet():
return flask.render_template('flask/stylesheet.html')
if __name__ == '__main__':
APP.debug=True
APP.run(port=f'{ PORTNUMBER }')
Loading…
Cancel
Save