soupboat setup

master
km0 3 years ago
parent e115e5d968
commit e6e486f6d6

@ -0,0 +1,13 @@
from setuptools import find_packages, setup
setup(
name='exquisite_branch',
version='1.0.0',
packages=find_packages(),
include_package_data=True,
zip_safe=False,
install_requires=[
'flask',
'shortuuid'
],
)

@ -0,0 +1,61 @@
import os
from flask import Flask
class PrefixMiddleware(object):
def __init__(self, app, prefix=''):
self.app = app
self.prefix = prefix
def __call__(self, environ, start_response):
if environ['PATH_INFO'].startswith(self.prefix):
environ['PATH_INFO'] = environ['PATH_INFO'][len(self.prefix):]
environ['SCRIPT_NAME'] = self.prefix
return self.app(environ, start_response)
else:
start_response('404', [('Content-Type', 'text/plain')])
return ["This url does not belong to the app.".encode()]
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
DATABASE=os.path.join(app.instance_path, 'exquisite'),
)
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile('config.py', silent=True)
else:
# load the test config if passed in
app.config.from_mapping(test_config)
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
from . import db
db.init_app(app)
from . import draw
app.register_blueprint(draw.bp)
from . import share
app.register_blueprint(share.bp)
from . import display
app.register_blueprint(display.bp)
from . import home
app.register_blueprint(home.bp)
app.wsgi_app = PrefixMiddleware(app.wsgi_app, prefix='/soupboat/xquisite')
return app

@ -0,0 +1,90 @@
from flask import (Blueprint, flash, g, redirect,
render_template, request, session, url_for)
from exquisite_branch.db import get_db
from werkzeug.exceptions import abort
from shortuuid import uuid
bp = Blueprint('draw', __name__, url_prefix='/draw')
@bp.route('/<parent>', methods=('GET', 'POST'))
def draw(parent=None):
db = get_db()
if request.method == 'POST':
content = request.form.get('content')
branch = request.form.get('branch')
if request.is_json:
data = request.get_json()
content = data['content']
branch = data['branch']
db.execute(
'INSERT INTO branches (content, parent, branch) VALUES (?, ?, ?)',
(content, parent, branch,)
)
db.commit()
print(url_for('share.share', branch=f"{branch}"))
return redirect(url_for('share.share', branch=branch))
branch = uuid()
previous = db.execute(
"SELECT content, branch, parent FROM branches"
" WHERE branch = ?",
(parent,)
).fetchone()
if previous is None:
abort(404, f"Previous with id {parent} doesn't exist")
return render_template('draw.html', parent=parent, content=previous['content'], branch=branch)
@bp.route('/last', methods=('GET', 'POST'))
def last():
branch = uuid()
db = get_db()
previous = db.execute(
'SELECT * FROM branches ORDER BY id DESC LIMIT 1'
).fetchone()
parent = previous['branch']
if request.method == 'POST':
content = request.form['content']
branch = request.form['branch']
db.execute(
'INSERT INTO branches (content, parent, branch) VALUES (?, ?, ?)',
(content, parent, branch,)
)
db.commit()
print(url_for('share.share', branch=f"{branch}"))
return redirect(url_for('share.share', branch=branch))
return render_template('draw.html', parent=parent, content=previous['content'], branch=branch)
@bp.route('/', methods=('GET', 'POST'))
def new():
db = get_db()
branch = uuid()
parent = 'NEW'
if request.method == 'POST':
content = request.form['content']
branch = request.form['branch']
db.execute(
'INSERT INTO branches (content, parent, branch) VALUES (?, ?, ?)',
(content, parent, branch,)
)
db.commit()
return redirect(url_for('share.share', branch=branch))
return render_template('draw.html', parent=parent, branch=branch)

@ -0,0 +1,7 @@
from flask import (Blueprint, render_template)
bp = Blueprint('home', __name__, url_prefix='/')
@bp.route('/')
def home():
return render_template('home.html')

@ -0,0 +1,9 @@
from flask import (Blueprint, flash, g, redirect,
render_template, request, session, url_for)
bp = Blueprint('share', __name__, url_prefix='/share')
@bp.route('/<branch>/')
def share(branch=None):
return render_template('share.html', branch=branch)

@ -1,6 +1,22 @@
import os import os
from flask import Flask from flask import Flask
class PrefixMiddleware(object):
def __init__(self, app, prefix=''):
self.app = app
self.prefix = prefix
def __call__(self, environ, start_response):
if environ['PATH_INFO'].startswith(self.prefix):
environ['PATH_INFO'] = environ['PATH_INFO'][len(self.prefix):]
environ['SCRIPT_NAME'] = self.prefix
return self.app(environ, start_response)
else:
start_response('404', [('Content-Type', 'text/plain')])
return ["This url does not belong to the app.".encode()]
def create_app(test_config=None): def create_app(test_config=None):
# create and configure the app # create and configure the app
@ -9,6 +25,7 @@ def create_app(test_config=None):
SECRET_KEY='dev', SECRET_KEY='dev',
DATABASE=os.path.join(app.instance_path, 'exquisite'), DATABASE=os.path.join(app.instance_path, 'exquisite'),
) )
if test_config is None: if test_config is None:
# load the instance config, if it exists, when not testing # load the instance config, if it exists, when not testing
@ -16,7 +33,7 @@ def create_app(test_config=None):
else: else:
# load the test config if passed in # load the test config if passed in
app.config.from_mapping(test_config) app.config.from_mapping(test_config)
# ensure the instance folder exists # ensure the instance folder exists
try: try:
os.makedirs(app.instance_path) os.makedirs(app.instance_path)
@ -38,4 +55,7 @@ def create_app(test_config=None):
from . import home from . import home
app.register_blueprint(home.bp) app.register_blueprint(home.bp)
app.wsgi_app = PrefixMiddleware(app.wsgi_app, prefix='/soupboat/xquisite')
return app return app

@ -27,6 +27,7 @@ def draw(parent=None):
(content, parent, branch,) (content, parent, branch,)
) )
db.commit() db.commit()
print(url_for('share.share', branch=f"{branch}"))
return redirect(url_for('share.share', branch=branch)) return redirect(url_for('share.share', branch=branch))
branch = uuid() branch = uuid()
@ -63,6 +64,7 @@ def last():
(content, parent, branch,) (content, parent, branch,)
) )
db.commit() db.commit()
print(url_for('share.share', branch=f"{branch}"))
return redirect(url_for('share.share', branch=branch)) return redirect(url_for('share.share', branch=branch))
return render_template('draw.html', parent=parent, content=previous['content'], branch=branch) return render_template('draw.html', parent=parent, content=previous['content'], branch=branch)

@ -4,7 +4,6 @@ from flask import (Blueprint, flash, g, redirect,
bp = Blueprint('share', __name__, url_prefix='/share') bp = Blueprint('share', __name__, url_prefix='/share')
@bp.route('/<branch>/')
@bp.route('/<branch>')
def share(branch=None): def share(branch=None):
return render_template('share.html', branch=branch) return render_template('share.html', branch=branch)

@ -0,0 +1,114 @@
// Great resource from https://stackoverflow.com/a/40700068
// Thank you ConnorFan
var strokeWidth = 2;
var bufferSize;
var svgElement = document.getElementById("svgElement");
var rect = svgElement.getBoundingClientRect();
var path = null;
var strPath;
var buffer = []; // Contains the last positions of the mouse cursor
svgElement.addEventListener("mousedown", function (e) {
bufferSize = document.getElementById("cmbBufferSize").value;
path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("fill", "none");
path.setAttribute("stroke", "currentColor");
path.setAttribute("stroke-width", strokeWidth);
buffer = [];
var pt = getMousePosition(e);
appendToBuffer(pt);
strPath = "M" + pt.x + " " + pt.y;
path.setAttribute("d", strPath);
svgElement.appendChild(path);
});
svgElement.addEventListener("mousemove", function (e) {
if (path) {
appendToBuffer(getMousePosition(e));
updateSvgPath();
}
});
svgElement.addEventListener("mouseup", function () {
if (path) {
path = null;
}
});
svgElement.addEventListener("mouseleave", function () {
if (path) {
path = null;
}
});
var getMousePosition = function (e) {
return {
x: e.pageX - rect.left,
y: e.pageY - rect.top,
};
};
var appendToBuffer = function (pt) {
buffer.push(pt);
while (buffer.length > bufferSize) {
buffer.shift();
}
};
// Calculate the average point, starting at offset in the buffer
var getAveragePoint = function (offset) {
var len = buffer.length;
if (len % 2 === 1 || len >= bufferSize) {
var totalX = 0;
var totalY = 0;
var pt, i;
var count = 0;
for (i = offset; i < len; i++) {
count++;
pt = buffer[i];
totalX += pt.x;
totalY += pt.y;
}
return {
x: totalX / count,
y: totalY / count,
};
}
return null;
};
var updateSvgPath = function () {
var pt = getAveragePoint(0);
if (pt) {
// Get the smoothed part of the path that will not change
strPath += " L" + pt.x + " " + pt.y;
// Get the last part of the path (close to the current mouse position)
// This part will change if the mouse moves again
var tmpPath = "";
for (var offset = 2; offset < buffer.length; offset += 2) {
pt = getAveragePoint(offset);
tmpPath += " L" + pt.x + " " + pt.y;
}
// Set the complete current path coordinates
path.setAttribute("d", strPath + tmpPath);
}
};
//
//
//
// SAVE THE BRANCH
const form = document.querySelector("form");
form.addEventListener("submit", () => {
let wrapper = document.createElement("div");
wrapper.appendChild(svgElement);
form["content"].value = wrapper.innerHTML;
return true;
});

@ -104,23 +104,11 @@ var updateSvgPath = function () {
// //
// SAVE THE BRANCH // SAVE THE BRANCH
const send = document.getElementById("send"); const form = document.querySelector("form");
send.addEventListener("click", (e) => saveSVG(e));
async function saveSVG(e) { form.addEventListener("submit", () => {
let wrapper = document.createElement("div"); let wrapper = document.createElement("div");
wrapper.appendChild(svgElement); wrapper.appendChild(svgElement);
form["content"].value = wrapper.innerHTML;
await fetch(`${svgElement.dataset.parent}`, { return true;
method: "POST", });
redirect: "follow",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
content: wrapper.innerHTML,
parent: svgElement.dataset.parent,
branch: svgElement.dataset.branch,
}),
}).then((res) => (window.location = res.url));
}

@ -0,0 +1,37 @@
<!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>Display</title>
<link rel="stylesheet" href="{{url_for('static', filename='css/variables.css')}}" />
<link rel="stylesheet" href="{{url_for('static', filename='css/global.css')}}" />
<link rel="stylesheet" href="{{url_for('static', filename='css/display.css')}}" />
</head>
<body>
<h1>Exquisite Branch</h1>
<div class="streams">
{% for stream in streams %}
<div
class="stream"
style="color: rgb({{ range(0, 255) | random }},{{ range(0, 255) | random }},{{ range(0, 255) | random }}); transform: translate({{ range(-5, 5) | random }}px, {{ range(-5, 5) | random }}px)"
>
{% for branch in stream %} {{branch['content'] | safe}} {%endfor%}
</div>
{%endfor%}
</div>
{{colors}}
<h2>Branches</h2>
<div class="branches">
{% for stream in streams %}
<div class="branch">
{% for branch in stream %} {{branch['content'] | safe}} {%endfor%}
</div>
{%endfor%}
</div>
</body>
</html>

@ -0,0 +1,54 @@
<!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>Draw</title>
<script src="{{url_for('static', filename='js/draw.js')}}" defer></script>
<link rel="stylesheet" href="{{url_for('static', filename='css/variables.css')}}" />
<link rel="stylesheet" href="{{url_for('static', filename='css/global.css')}}" />
<link rel="stylesheet" href="{{url_for('static', filename='css/draw.css')}}" />
</head>
<body>
<h1>Draw</h1>
<div id="divSmoothingFactor">
<label for="cmbBufferSize">Buffer size:</label>
<select id="cmbBufferSize">
<option value="1">1 - No smoothing</option>
<option value="4">4 - Sharp curves</option>
<option value="8" selected="selected">8 - Smooth curves</option>
<option value="12">12 - Very smooth curves</option>
<option value="16">16 - Super smooth curves</option>
<option value="20">20 - Hyper smooth curves</option>
</select>
</div>
<div id="svg-container">
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
id="svgElement"
x="0px"
y="0px"
width="500px"
height="500px"
viewBox="0 0 500 500"
enable-background="new 0 0 500 500"
xml:space="preserve"
data-parent="{{parent or None}}"
data-branch="{{branch}}"
></svg>
<div id="previous">{% if content %} {{content|safe}} {% endif %}</div>
</div>
<form method="POST">
<input type="hidden" name="branch" value="{{branch}}" />
<input type="hidden" name="content" />
<input type="submit" />
</form>
</body>
</html>

@ -0,0 +1,20 @@
<!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>Share</title>
<link rel="stylesheet" href="{{url_for('static', filename='css/variables.css')}}" />
<link rel="stylesheet" href="{{url_for('static', filename='css/global.css')}}" />
<link rel="stylesheet" href="{{url_for('static', filename='css/share.css')}}" />
</head>
<body>
<div class="share">
Send this link to your friends:
<a href="{{url_for('draw.draw', parent=branch)}}"
>{{url_for('draw.draw', parent=branch)}}</a
>
</div>
</body>
</html>

@ -45,6 +45,10 @@
<div id="previous">{% if content %} {{content|safe}} {% endif %}</div> <div id="previous">{% if content %} {{content|safe}} {% endif %}</div>
</div> </div>
<button id="send">Send</button> <form method="POST">
<input type="hidden" name="branch" value="{{branch}}" />
<input type="hidden" name="content" />
<input type="submit" />
</form>
</body> </body>
</html> </html>

Loading…
Cancel
Save