Compare commits

..

1 Commits
main ... master

Author SHA1 Message Date
km0 059880cd39 text version init 1 year ago

@ -0,0 +1,5 @@
TODO: comment the code
TODO: remove new from homepage?
or
TODO: add per-new level in the xquisite branches (this makes more sense!)

@ -1,7 +1,6 @@
import os
from flask import Flask, send_from_directory
from . import prefix
from flask_mako import MakoTemplates
def create_app(test_config=None):
@ -28,8 +27,6 @@ def create_app(test_config=None):
from . import db
db.init_app(app)
mako = MakoTemplates(app)
@app.route("/favicon.ico")
def favicon():
return send_from_directory(
@ -48,9 +45,6 @@ def create_app(test_config=None):
from . import home
app.register_blueprint(home.bp)
from . import api
app.register_blueprint(api.bp)
app.wsgi_app = prefix.PrefixMiddleware(
app.wsgi_app, prefix=os.environ.get("URL_PREFIX", ''))

@ -1,63 +0,0 @@
from flask import (Blueprint, jsonify)
from exquisite_branch.db import get_db
bp = Blueprint('api', __name__, url_prefix='/api/v1')
def makeTree(entries):
if len(entries) > 1:
last = entries.pop()
if last['parent'] != 'NEWS':
parent_index = next(
(index for (index, entry) in enumerate(entries) if entry['id'] == last['parent']),
None)
entries[parent_index].setdefault('branches', []).append(last)
makeTree(entries)
return entries
@bp.route('/trees/')
def list():
db = get_db()
trees = db.execute('SELECT * FROM trees').fetchall()
t = []
for tree in trees:
t.append(tree['slug'])
response = jsonify(t)
response.headers.add("Access-Control-Allow-Origin", "*")
return response
@bp.route('tree/<tree>/')
def tree(tree=None):
db = get_db()
tree_info = db.execute('SELECT * FROM trees WHERE slug = ?', (tree,)).fetchone()
entries = db.execute(
"SELECT content, id, parent, username FROM branches WHERE tree = ?",
(tree,)
).fetchall()
r = {
"id": tree_info['id'],
"slug": tree_info['slug'],
"name": tree_info['name'],
}
# convert sqlite entries to list of normal dict
branches = []
for entry in entries:
branches.append({**entry})
# make tree:
# while list is not empty
# start from the last one
# insert it in its parent if not NEW
# remove it from the list
r['branches'] = makeTree(branches)
response = jsonify(r)
response.headers.add("Access-Control-Allow-Origin", "*")
return response

@ -1,6 +1,5 @@
from flask import (Blueprint, flash, g, redirect,
request, session, url_for, jsonify)
from flask_mako import render_template
render_template, request, session, url_for)
from exquisite_branch.db import get_db
@ -9,24 +8,28 @@ bp = Blueprint('display', __name__, url_prefix='/display')
@bp.route('/')
def list():
db = get_db()
trees = db.execute('SELECT * FROM trees').fetchall()
return render_template('list.html', trees=trees)
@bp.route('/<tree>/')
def display(tree=None):
def display():
db = get_db()
branches = db.execute(
"SELECT content, id, parent, username FROM branches WHERE tree = ?",
(tree,)
"SELECT content, branch, parent, username FROM branches"
).fetchall()
return render_template('display.html', tree=tree, branches=branches)
streams = []
for branch in branches[::-1]:
if branch not in flatten(streams):
stream = [branch]
parent = branch['parent']
while parent != 'NEW':
current = next(
(x for x in branches if x['branch'] == parent), None)
parent = current['parent']
stream.append(current)
streams.append(stream[::-1])
return render_template('display.html', branches=branches, streams=streams)
@bp.route('/<tree>/js/')
def js(tree=None):
return render_template('display_js.html', tree=tree)
def flatten(t):
return [item for sublist in t for item in sublist]

@ -0,0 +1,93 @@
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')
username = request.form.get('username')
if request.is_json:
data = request.get_json()
content = data['content']
branch = data['branch']
username = data['username']
db.execute(
'INSERT INTO branches (content, parent, branch, username) VALUES (?, ?, ?, ?)',
(content, parent, branch, username)
)
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']
username = request.form['username']
db.execute(
'INSERT INTO branches (content, parent, branch, username) VALUES (?, ?, ?, ?)',
(content, parent, branch, username)
)
db.commit()
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']
username = request.form['username']
db.execute(
'INSERT INTO branches (content, parent, branch, username) VALUES (?, ?, ?, ?)',
(content, parent, branch, username)
)
db.commit()
return redirect(url_for('share.share', branch=branch))
return render_template('draw.html', parent=parent, branch=branch)

@ -1,17 +1,9 @@
DROP TABLE IF EXISTS branches;
DROP TABLE IF EXISTS trees;
CREATE TABLE branches (
id TEXT PRIMARY KEY NOT NULL,
parent TEXT NOT NULL,
content TEXT NOT NULL,
username TEXT,
tree TEXT NOT NULL,
FOREIGN KEY (tree) REFERENCES trees(slug)
);
CREATE TABLE trees (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
slug TEXT NOT NULL UNIQUE
);
branch TEXT NOT NULL,
content TEXT NOT NULL,
parent TEXT NOT NULL,
username TEXT
);

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

@ -1,51 +1,26 @@
header {
padding: 32px;
max-width: 60ch;
}
header h1,
header p {
margin: 0;
}
.container{
display: block;
padding: 32px;
}
.stream,
.streams{
/* white-space: nowrap; */
position: relative;
display: inline-block;
width: 40ch;
.streams {
overflow-x: auto;
overflow-y: hidden;
position: relative;
height: 500px;
}
.text-container {
.stream {
white-space: nowrap;
position: absolute;
top: 0;
left: 0;
display: inline-block;
width: 40ch;
pointer-events: all;
background: linear-gradient(0.25turn, rgba(255,255,255,0), rgba(255,255,255,0.5));
}
.text-container:hover{
background-color: white;
color: var(--background);
z-index: 100;
.svg-container {
display: inline-block;
margin: 0;
padding: 0;
position: relative;
}
.author {
position: relative;
position: absolute;
left: 50%;
bottom: 50px;
font-size: 1rem;
background-color: white;
}
@ -59,6 +34,6 @@ margin: 0;
white-space: nowrap;
}
a:hover {
color: currentColor;
.branch svg {
border-top: 1px solid currentColor;
}

@ -0,0 +1,32 @@
svg {
width: 500px;
height: 500px;
background-color: white;
}
#svg-container {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
#previous {
position: absolute;
top: 0;
transform: translateX(-100%);
}
#previous svg {
background: none;
border: none;
}
#previous::after {
content: "";
width: 100%;
height: 100%;
left: 0;
position: absolute;
transform: translateX(-25%);
background-color: var(--background);
}

@ -13,11 +13,10 @@ body {
a {
color: currentColor;
text-decoration: none;
user-select: all;
}
a:hover {
color: white;
color: tomato;
}
nav {
@ -27,33 +26,3 @@ nav {
nav > * + * {
margin-left: 1ch;
}
header, main {
margin: 32px;
}
main form {
display: flex;
flex-direction: column;
gap: 16px;
max-width: 80ch;
align-items: flex-start;
}
form textarea,
form input {
font-size: 1rem;
font-family: serif;
border: none;
padding: 0.5em;
}
textarea:focus,
input:focus {
outline: dotted white;
}
textarea {
width: 80ch;
height: 25ch;
}

@ -4,7 +4,7 @@ header {
}
header object {
width: 500px;
width: 100vmin;
height: auto;
margin: 0 auto;
}

@ -1,4 +1,4 @@
:root {
--background: #2a9d8f;
--background: #edd;
--color: black;
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

@ -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;
});

@ -14,7 +14,7 @@
<nav>
{%block nav %}
<a href="{{url_for('home.home')}}">Home</a>
<a href="{{url_for('display.list')}}">Results</a>
<a href="{{url_for('display.display')}}">Results</a>
{% endblock %}
</nav>
{%block contents %} {%endblock%}

@ -1,22 +0,0 @@
<!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>Exquisite Branch</title>
<link rel="stylesheet" href="${url_for('static', filename='css/variables.css')}" />
<link rel="stylesheet" href="${url_for('static', filename='css/global.css')}" />
<%block name='head' %></%block>
</head>
<body>
<nav>
<%block name='nav' >
<a href="${url_for('home.home')}">Home</a>
<a href="${url_for('display.list')}">Results</a>
</%block>
</nav>
${self.body()}
</body>
</html>

@ -1,44 +1,49 @@
<%inherit file="base_mako.html" />
<%block name="head">
<link rel="stylesheet" href="${url_for('static', filename='css/display.css')}">
</%block>
<header>
<h1>Exquisite Excerpts</h1>
<p>Here texts branch and follow their own path. Click on an excerpt to create a new branch and continue toward new meanings. </p>
</header>
<main class="container">
<% from random import random %>
<% from collections import defaultdict %>
<% transform = {'NEW': ''} %>
<% visited = defaultdict(int) %>
<div class="stream">
% for branch in branches:
<% visited[branch['parent']] += 1 %>
% if visited[branch['parent']] > 1:
<% steer = (random() - 0.5) * 0.25 %>
% else:
<% steer = 0 %>
% endif
<% transform[branch['id']] = f'{transform[branch["parent"]]} rotate({random() * 0.04 + steer}turn) translateX(100%)' %>
<div class="text-container" style="transform: ${transform[branch['parent']]}">
<a href="${url_for('write.write', tree=tree, parent=branch['id'], _external=True, _scheme='http')}" target="__blank">
${branch['content']}
</a>
<span class="author">${branch['username']}</span>
{%extends 'base.html' %} {%block head %}
<link rel="stylesheet" href="{{url_for('static', filename='css/display.css')}}" />
<title>Display</title>
{%endblock%} {%block nav%}
<a href="{{url_for('home.home')}}">Home</a>
<a href="{{url_for('write.last')}}">Write</a>
{%endblock%} {%block contents%}
<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 %}
<div class="svg-container">
{{branch['content'] | safe}}
<span
class="author"
style="transform: translate({{ range(-50, 50) | random }}px, {{ range(-10, 10) | random }}px"
>
{{ branch['username']}}
</span>
</div>
{%endfor%}
</div>
{%endfor%}
</div>
{{colors}}
<h2>Branches</h2>
<div class="branches">
{% for stream in streams %}
<div class="branch">
{% for branch in stream %}
<div class="svg-container">
{{branch['content'] | safe}}
<span class="author"> {{ branch['username']}} </span>
</div>
% endfor
{%endfor%}
</div>
</main>
{%endfor%}
</div>
{%endblock%}

@ -1,64 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${tree}</title>
<style>
.container {
font-size: 24px;
}
.child {
display: flex;
flex-direction: row;
align-items: flex-start;
background-color: rgba(255,0,0,0.075);
border: 1px solid rgba(255,0,0,0.1);
gap: 0.5em;
padding: 0.5em;
}
.child+.child {
flex-direction: column;
}
</style>
</head>
<body>
<h1 id="tree">${tree}</h1>
<div class="container"></div>
<script>
const title = document.querySelector('#tree').innerText
const slug = title.innerText
const container = document.querySelector('.container')
const display = (element, tree) => {
if(tree.branches){
let content = document.createElement('div')
content.classList.add('child')
if (tree.content) {
content.innerHTML = tree.content
}
element.appendChild(content)
for (const b of tree.branches) {
display(content, b)
}
}
}
fetch(`/api/v1/tree/${tree}/`).then(res=>res.json()).then(res=>display(container, res))
</script>
</body>
</html>

@ -0,0 +1,51 @@
{%extends 'base.html' %} {%block head %}
<title>Draw</title>
<link rel="stylesheet" href="{{url_for('static', filename='css/draw.css')}}" />
<script src="{{url_for('static', filename='js/draw.js')}}" defer></script>
{%endblock%} {%block nav%}
<a href="{{url_for('home.home')}}">Home</a>
<a href="{{url_for('display.display')}}">Results</a>
{%endblock%} {%block contents%}
<h1>Draw</h1>
<div id="divSmoothingFactor">
<label for="cmbBufferSize">Smoothing</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="text" name="username" placeholder="Name" />
<input type="submit" />
</form>
{%endblock%}

@ -12,15 +12,16 @@
<body>
<header class="title">
<object
data="{{url_for('static', filename='img/title.svg')}}"
data="{{url_for('static', filename='img/logo.svg')}}"
type="image/svg+xml"
></object>
<h1>really exquisite indeed</h1>
</header>
<main>
<a href="{{url_for('write.new')}}">Start new</a> <br />
<a href="{{url_for('write.last')}}">Continue from last</a> <br />
<a href="{{url_for('display.list')}}">Display results</a>
<a href="{{url_for('display.display')}}">Display results</a>
</main>
</body>
</html>

@ -1,28 +0,0 @@
<%inherit file="base_mako.html" />
<%block name="head">
<link rel="stylesheet" href="${url_for('static', filename='css/display.css')}">
</%block>
<header>
<h1>Exquisite Excerpts</h1>
<p>Here is a list of all the writings trees</p>
</header>
<main class="container">
<ul>
% for tree in trees:
<li class="tree">
<a href="${url_for('display.display', tree=tree['slug'])}">
${tree['name']}
</a>
</li>
% endfor
</ul>
</main>

@ -5,16 +5,15 @@
{%endblock%} {%block nav %}
<a href="{{url_for('home.home')}}">Home</a>
<a href="{{url_for('display.display', tree=tree)}}">Results</a>
<a href="{{url_for('display.display')}}">Results</a>
{% endblock %} {%block contents %}
<div class="share">
Send this link to your friends:
<a
href="{{url_for('write.write',tree=tree, parent=id)}}"
data-copy="{{ url_for('write.write', tree=tree, parent=id, _external=True, _scheme='https')}}"
>{{ url_for('write.write', tree=tree, parent=id, _external=True, _scheme='https')}}</a
href="{{url_for('write.write', parent=branch)}}"
data-copy="{{ url_for('write.write', parent=branch, _external=True, _scheme='https')}}"
>{{ url_for('write.write', parent=branch, _external=True, _scheme='https')}}</a
>
</div>

@ -1,49 +1,21 @@
{%extends 'base.html' %} {%block head %}
<title>Draw</title>
<title>Write</title>
<link rel="stylesheet" href="{{url_for('static', filename='css/write.css')}}" />
<script src="{{url_for('static', filename='js/draw.js')}}" defer></script>
{%endblock%} {%block nav%}
<a href="{{url_for('home.home')}}">Home</a>
{% if tree is undefined %}
<a href="{{url_for('display.list')}}">See all</a>
{% else %}
<a href="{{url_for('display.display', tree=tree)}}">See tree</a>
{% endif %}
<a href="{{url_for('display.display')}}">Results</a>
{%endblock%} {%block contents%}
<!-- mmm i dont remember what i was using these two things for! -->
<!-- data-parent="{{parent or None}}" -->
<!-- data-id="{{id}}" -->
<!-- Maybe to create links in the display page? -->
<header>
<h1>Write</h1>
</header>
<main>
<div id="previous">{% if content %} {{content|safe}} {% endif %}</div>
<form method="POST">
<input type="hidden" name="id" value="{{id}}" />
{% if tree is undefined %}
<input type="text" name="tree_name" placeholder="Tree name" required />
<br/>
{% endif %}
<textarea name="content" placeholder="Write here..." required></textarea>
<br/>
<input type="text" name="username" placeholder="Author" />
<br/>
<input type="submit" value="Submit" />
<input type="hidden" name="branch" value="{{branch}}" />
<textarea name="content" ></textarea>
<input type="text" name="username" placeholder="Name" />
<input type="submit" />
</form>
</main>
{%endblock%}

@ -1,102 +1,93 @@
from flask import (Blueprint, redirect, render_template, request, url_for)
from flask import (Blueprint, redirect,
render_template, request, url_for)
from exquisite_branch.db import get_db
from werkzeug.exceptions import abort
from werkzeug.utils import secure_filename
from shortuuid import uuid
bp = Blueprint('write', __name__, url_prefix='/write')
@bp.route('/<tree>/<parent>', methods=('GET', 'POST'))
def write(tree=None, parent=None):
@bp.route('/<parent>', methods=('GET', 'POST'))
def write(parent=None):
db = get_db()
if request.method == 'POST':
content = request.form.get('content')
id = request.form.get('id')
branch = request.form.get('branch')
username = request.form.get('username')
if request.is_json:
data = request.get_json()
content = data['content']
id = data['id']
branch = data['branch']
username = data['username']
db.execute(
'INSERT INTO branches (content, parent, id, username, tree) VALUES (?, ?, ?, ?, ?)',
(content, parent, id, username, tree)
'INSERT INTO branches (content, parent, branch, username) VALUES (?, ?, ?, ?)',
(content, parent, branch, username)
)
db.commit()
print(url_for('share.share', tree=tree, id=id))
return redirect(url_for('share.share', tree=tree, id=id))
print(url_for('share.share', branch=f"{branch}"))
return redirect(url_for('share.share', branch=branch))
id = uuid()
branch = uuid()
previous = db.execute(
"SELECT content, id, parent FROM branches"
" WHERE id = ? AND tree = ?",
(parent, tree)
"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('write.html', tree=tree, parent=parent, content=previous['content'], id=id)
return render_template('write.html', parent=parent, content=previous['content'], branch=branch)
@bp.route('/last', methods=('GET', 'POST'))
def last():
id = uuid()
branch = uuid()
db = get_db()
previous = db.execute(
'SELECT * FROM branches ORDER BY id DESC LIMIT 1'
).fetchone()
parent = previous['id']
tree = previous['tree']
parent = previous['branch']
if request.method == 'POST':
content = request.form['content']
id = request.form['id']
branch = request.form['branch']
username = request.form['username']
db.execute(
'INSERT INTO branches (content, parent, id, username, tree) VALUES (?, ?, ?, ?, ?)',
(content, parent, id, username, tree)
'INSERT INTO branches (content, parent, branch, username) VALUES (?, ?, ?, ?)',
(content, parent, branch, username)
)
db.commit()
return redirect(url_for('share.share', tree=tree, id=id))
return redirect(url_for('share.share', branch=branch))
return render_template('write.html', tree=tree, parent=parent, content=previous['content'], id=id)
return render_template('write.html', parent=parent, content=previous['content'], branch=branch)
@bp.route('/', methods=('GET', 'POST'))
def new():
db = get_db()
id = uuid()
branch = uuid()
parent = 'NEW'
if request.method == 'POST':
content = request.form['content']
id = request.form['id']
username = request.form['username']
tree_name = request.form['tree_name']
if tree_name == '':
return redirect(url_for('write.new'))
branch = request.form['branch']
tree = secure_filename(tree_name)
db.execute(
'INSERT INTO trees (name, slug) VALUES (?, ?)',
(tree_name, tree)
)
username = request.form['username']
db.execute(
'INSERT INTO branches (content, parent, id, username, tree) VALUES (?, ?, ?, ?, ?)',
(content, parent, id, username, tree)
'INSERT INTO branches (content, parent, branch, username) VALUES (?, ?, ?, ?)',
(content, parent, branch, username)
)
db.commit()
return redirect(url_for('share.share', tree=tree, id=id))
return redirect(url_for('share.share', branch=branch))
return render_template('write.html', parent=parent, id=id)
return render_template('write.html', parent=parent, branch=branch)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

@ -1,61 +0,0 @@
# Exquisite Excerpts
A branching version of the [exquisite corpse](https://en.wikipedia.org/wiki/Exquisite_corpse) game, forked from the [exquisite branch](https://git.xpub.nl/kamo/exquisite-branch) drawing app developed for [SI17](https://issue.xpub.nl/17/).
Write something, upload it and send the link to someone else: they will continue from your excerpt. With a catch: if you send to just one person the chain will continue linearly, but send it to more people and things will start branching in different directions.
Could be a writing machine to work on the collective pubblication for the graduation, inspired by what [Kim wrote here](https://pad.xpub.nl/p/gradcollectivexpub).
## Install
Clone the repository.
```
git clone https://git.xpub.nl/kamo/exex.git
```
Create a virtual environment.
```
python3 -m venv venv
```
Install the requirements with `pip` and the `requirements.txt` file.
```
pip install -r requirements.txt
```
Create an `.env` file in the root folder of the project with the following variables:
```
DEBUG=True
FLASK_ENV=development
FLASK_APP=exquisite_branch
```
Before running the app for the first time, be sure to initialize the database. __Watch out:__ this will delete the previous instance of the database along with its contents!
```
flask init-db
```
After initializing the database you can run the flask application
```
flask run
```
## Overview
Exex saves contents in a database, and join them together in a branching version of the exquisite corpse.
The original exquisite corpse data structure is something similar to a linked list, where every drawing is connected to the previous one. Contents in _exex_ are saved in a database with the same principle.
![Diagram with the situationship of the entries](linked.jpg)
Each entry in the database has the following properties:
- `id`: a unique identifier for every entry
- `branch`: a random name for the excerpt
- `parent`: the random name of the previous excerpt
- `content`: the actual content of the writings
- `username`: the author of the excerpt
When generating the display page, every entry look up its `parent` property to position itself after that.
Mhh should rewrite this better bc is super convoluted ahah.

@ -1,14 +0,0 @@
autopep8==1.6.0
click==8.0.3
colorama==0.4.4
Flask==2.0.2
Flask-Mako==0.4
itsdangerous==2.0.1
Jinja2==3.0.3
Mako==1.2.0
MarkupSafe==2.0.1
pycodestyle==2.8.0
python-dotenv==0.19.2
shortuuid==1.0.8
toml==0.10.2
Werkzeug==2.0.3

@ -1,7 +1,7 @@
from setuptools import find_packages, setup
setup(
name='exquisite_excerpts',
name='exquisite_branch',
version='1.0.0',
packages=find_packages(),
include_package_data=True,
@ -9,7 +9,6 @@ setup(
install_requires=[
'flask',
'shortuuid',
'python-dotenv',
'flask-mako'
'python-dotenv'
],
)

Loading…
Cancel
Save